リクエスト処理

このガイドでは、スキル開発用にSDKで使用できる次のリクエスト処理コンポーネントについて説明します。

ハンドラー入力

リクエストハンドラー、リクエストと応答のインターセプター、例外ハンドラーにはすべて、呼び出し時に共通の HandlerInput オブジェクトが渡されます。このオブジェクトには、リクエスト処理に有効な各種エンティティが含まれます。以下はその例です。

  • request_envelope :スキルに送信されるリクエスト本文全体を含みます。
  • attributes_manager :リクエスト、セッション、永続アトリビュートへのアクセスを提供します。
  • service_client_factory : Alexa APIの呼び出しが可能なサービスクライアントを構築します。
  • response_builder : 応答を作成するヘルパー関数を含みます。
  • context :ホストコンテナが渡すオプションのcontextオブジェクトを提供します。たとえば、AWS Lambdaで実行されるスキルの場合は、AWS Lambda関数のcontextオブジェクトになります。

リクエストハンドラー

リクエストハンドラーは、受信するさまざまなタイプのAlexaリクエストの処理を担当します。カスタムリクエストハンドラーを作成する方法は2つあります。

  • AbstractRequestHandler クラスを実装する。
  • スキルビルダーrequest_handler デコレーターを使用してカスタムハンドル関数をデコレートする。

インターフェース

AbstractRequestHandlerクラス

AbstractRequestHandler クラスの使用を予定している場合は、次のメソッドを実装する必要があります。

  • can_handlecan_handle は、SDKによって呼び出され、指定されたハンドラーが受け取ったリクエストを処理できるかどうかを判断します。この関数はハンドラー入力オブジェクトを受け付け、ブール型を返すように想定されています。メソッドがTrueを返せば、ハンドラーによってリクエストが正常に処理されたと考えられます。Falseを返す場合、ハンドラーが入力リクエストを処理できず、したがって実行されず完了もしなかったと考えられます。HandlerInput オブジェクトにはさまざまなアトリビュートがあるため、リクエストを正常に処理できるかどうかをSDKが判別するための任意の条件を作成できます。
  • handlehandle メソッドは、リクエストハンドラーを呼び出すときにSDKによって呼び出されます。この関数には、ハンドラーのリクエスト処理ロジックが含まており、ハンドラー入力を受け取り、応答オブジェクトを返します。
class AbstractRequestHandler(object):
    @abstractmethod
    def can_handle(self, handler_input):
        # type: (HandlerInput) -> bool
        pass

    @abstractmethod
    def handle(self, handler_input):
        # type: (HandlerInput) -> Response
        pass

以下は、HelloWorldIntent を呼び出すことができるリクエストハンドラークラスの例です。

from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_model.ui import SimpleCard

class HelloWorldIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return handler_input.request_envelope.request.type == "IntentRequest"
          and handler_input.request_envelope.request.intent.name == "HelloWorldIntent"

    def handle(self, handler_input):
        speech_text = "Hello World";

        return handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("Hello World", speech_text)).response

can_handle 関数は受け取るリクエストが IntentRequest かどうかを検出し、インテント名が HelloWorldIntent の場合にtrueを返します。handle 関数は、基本的な「こんにちは」という応答を生成して返します。

SkillBuilderのrequest_handlerデコレーター

SkillBuilderクラスの request_handler デコレーターは、AbstractRequestHandler クラスに搭載されたカスタムラッパーであり、カスタムでデコレートされた任意の関数と同じ機能を提供します。ただし、デコレーターを使用するには考慮事項が2つあります。

  • デコレーターは can_handle_func パラメーターを取ります。これは AbstractRequestHandlercan_handle メソッドに似たものです。渡される値はハンドラー入力オブジェクトを受け付け、ブール型値を返す関数である必要があります。
  • デコレートされた関数が受け付けるパラメーターはハンドラー入力1つのみであり、Response オブジェクトを返します。
class SkillBuilder(object):
    ....
    def request_handler(self, can_handle_func):
        def wrapper(handle_func):
            # wrap the can_handle and handle into a class
            # add the class into request handlers list
            ....
        return wrapper

以下は、HelloWorldIntent を処理できるリクエストハンドラー関数の例です。

from ask_sdk_core.utils import is_intent_name
from ask_sdk_model.ui import SimpleCard
from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

@sb.request_handler(can_handle_func = is_intent_name("HelloWorldIntent"))
def hello_world_intent_handler(handler_input):
    speech_text = "Hello World!"

    return handler_input.response_builder.speak(speech_text).set_card(
        SimpleCard("Hello World", speech_text)).response

is_intent_name 関数はstringパラメーターを受け取り無名関数を返します。この無名関数は、HandlerInput を入力パラメーターとして受け取って、HandlerInput の受信リクエストが IntentRequest であるかを確認し、インテント名が string に渡されているものであればそれを返します。この例では HelloWorldIntent です。handle 関数は、基本的な「こんにちは」という応答を生成して返します。

リクエストハンドラーの登録と処理

SDKは、リクエストハンドラーで、スキルビルダーに指定された順序で can_handle 関数を呼び出します。

AbstractRequestHandler クラスを使用する方法に従っている場合、次の方法でリクエストハンドラーを登録できます

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# Implement FooHandler, BarHandler, BazHandler classes

sb.add_request_handler(FooHandler())
sb.add_request_handler(BarHandler())
sb.add_request_handler(BazHandler())

request_handler デコレーターを使用する方法に従っている場合、ハンドラー関数を明示的に登録する必要はありません。スキルビルダーインスタンスを使用してすでにデコレートされています。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# decorate foo_handler, bar_handler, baz_handler functions

上記の例では、SDKが以下の順序でリクエストハンドラーを呼び出します。

  1. FooHandler クラス / foo_handler 関数
  2. BarHandler クラス / bar_handler 関数
  3. BazHandle rクラス / baz_handler 関数

SDKは、指定されたリクエストを処理できる最初のハンドラーを常に選択します。この例では、FooHandler クラス / foo_handler 関数および BarHandler``クラス ``bar_handler 関数のどちらも指定のリクエストを処理できる場合、FooHandler クラス/foo_handler 関数が常に呼び出されます。リクエストハンドラーのデザインや登録を行う際には、この点を考慮に入れてください。

例外ハンドラー

例外ハンドラーはリクエストハンドラーに似ていますが、リクエストではなく1つまたは複数のタイプの例外を処理します。リクエストの処理中に未処理の例外がスローされると、SDKが例外ハンドラーを呼び出します。

ハンドラーはハンドラー入力オブジェクトに加えて、入力リクエストの処理中に発生した例外にもアクセスできます。そのため、ハンドラーが該当する例外の処理方法を判別しやすくなります。

リクエストハンドラーと同様に、カスタムリクエストインターセプターも2通りの方法で実装できます。

  • AbstractExceptionHandler クラスを実装する。
  • スキルビルダーexception_handler デコレーターを使用してカスタム例外ハンドル関数をデコレートする。

インターフェース

AbstractExceptionHandlerクラス

AbstractExceptionHandler クラスの使用を予定している場合は、次のメソッドを実装する必要があります。

  • can_handlecan_handle メソッドはSDKによって呼び出され、指定されたハンドラーが例外を処理できるかどうかを判断します。ハンドラーが例外を処理できる場合はTrue、できない場合はFalseを返します。catch-allハンドラーを作成する場合は常に True を返します。
  • handlehandle メソッドは例外ハンドラーを呼び出すときにSDKによって呼び出されます。この関数には、例外処理ロジックがすべて含まれ、応答オブジェクトを返します。
class AbstractExceptionHandler(object):
    @abstractmethod
    def can_handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> bool
        pass

    @abstractmethod
    def handle(self, handler_input, exception):
        # type: (HandlerInput, Exception) -> Response
        pass

以下は、名前に「AskSdk」が含まれる例外をすべて処理する例外ハンドラーの例です。

class AskExceptionHandler(AbstractExceptionHandler):
     def can_handle(self, handler_input, exception):
         return 'AskSdk' in exception.__class__.__name__

     def handle(self, handler_input, exception):
         speech_text = "Sorry, I am unable to figure out what to do. Try again later!!";

         return handler_input.response_builder.speak(speech_text).response

ハンドラーの can_handle メソッドは、受け取る例外の名前が「AskSdk」で始まる場合にTrueを返します。handle メソッドは、ユーザーに正常な例外応答を返します。

SkillBuilderのexception_handlerデコレーター

SkillBuilderクラスの exception_handler デコレーターは、AbstractExceptionHandler クラスに搭載されたカスタムラッパーであり、カスタムでデコレートされた任意の関数と同じ機能を提供します。ただし、デコレーターを使用するには以下の2点を考慮してください。

  • デコレーターは can_handle_func パラメーターを取ります。これは AbstractExceptionHandlercan_handle メソッドに似たものです。渡される値はハンドラー入力オブジェクトを例外インスタンスとして受け付け、ブール型値を返す関数である必要があります。
  • デコレートされた関数が受け付けるパラメーターはハンドラー入力オブジェクトおよび例外オブジェクトの2つのみです。応答オブジェクトが返されます。
class SkillBuilder(object):
    ....
    def exception_handler(self, can_handle_func):
        def wrapper(handle_func):
            # wrap the can_handle and handle into a class
            # add the class into exception handlers list
            ....
        return wrapper

以下は、名前に「AskSdk」が含まれる例外をすべて処理する例外ハンドラー関数の例です。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

@sb.exception_handler(can_handle_func = lambda input, e: 'AskSdk' in e.__class__.__name__)
def ask_exception_intent_handler(handler_input, exception):
    speech_text = "Sorry, I am unable to figure out what to do. Try again later!!";

    return handler_input.response_builder.speak(speech_text).response

例外ハンドラーの登録と処理

AbstractExceptionHandler クラスを使用する方法に従っている場合、次の方法でリクエストハンドラーを登録できます

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# Implement FooExceptionHandler, BarExceptionHandler, BazExceptionHandler classes

sb.add_exception_handler(FooExceptionHandler())
sb.add_exception_handler(BarExceptionHandler())
sb.add_exception_handler(BazExceptionHandler())

exception_handler デコレーターを使用する方法に従っている場合、ハンドラー関数を明示的に登録する必要はありません。スキルビルダーインスタンスを使用してすでにデコレートされています。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# decorate foo_exception_handler, bar_exception_handler, baz_exception_handler functions

リクエストハンドラーと同様に、例外ハンドラーはスキルで指定した順序で実行されます。

リクエストと応答のインターセプター

SDKは、一致する RequestHandler実行前実行後に実行するリクエストと応答のグローバルインターセプターをサポートします。

リクエストインターセプター

グローバルリクエストインターセプターは、登録されたリクエストハンドラーの処理前に、ハンドラー入力オブジェクトを受け付けて処理します。リクエストハンドラーと同様に、カスタムリクエストインターセプターも2通りの方法で実装できます。

  • AbstractRequestInterceptor クラスを実装する。
  • スキルビルダーglobal_request_interceptor デコレーターを使用してカスタム処理関数をデコレートする。

インターフェース

AbstractRequestInterceptorクラス

AbstractRequestInterceptor クラスを使用するには、処理メソッドを実装する必要があります。このメソッドはハンドラー入力インスタンスを取得し、何も返しません。

class AbstractRequestInterceptor(object):
    @abstractmethod
    def process(self, handler_input):
        # type: (HandlerInput) -> None
        pass

以下は、Alexaサービスが受け取ったリクエストを、処理の前にAWS CloudWatchログに書き込むリクエストインターセプタークラスの例です。

from ask_sdk_core.dispatch_components import AbstractRequestInterceptor

class LoggingRequestInterceptor(AbstractRequestInterceptor):
    def process(self, handler_input):
        print("Request received: {}".format(handler_input.request_envelope.request))
SkillBuilderのglobal_request_interceptorデコレーター

SkillBuilderクラスの global_request_interceptor デコレーターは、AbstractRequestInterceptor クラスに搭載されたカスタムラッパーであり、カスタムでデコレートされた任意の関数と同じ機能を提供します。ただし、デコレーターを使用するには以下の2点を考慮してください。

  • デコレーターはスキルビルダーインスタンスを必要とするため、インターセプターを登録するには、関数名としてではなく関数として呼び出される必要があります。
  • デコレートされた関数が受け付けるパラメーターはハンドラー入力オブジェクト1つのみであり、関数からの戻り値はキャプチャーされません。
class SkillBuilder(object):
    ....
    def global_request_interceptor(self):
        def wrapper(process_func):
            # wrap the process_func into a class
            # add the class into request interceptors list
            ....
        return wrapper

以下は、リクエストインターセプターとして使用できるログ記録関数の例です。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

@sb.global_request_interceptor()
def request_logger(handler_input):
    print("Request received: {}".format(handler_input.request_envelope.request))

リクエストインターセプターの登録と処理

リクエストのインターセプターは、リクエストハンドラーが受け取ったリクエストを処理する直前に呼び出されます。ハンドラー入力のアトリビュートマネージャー内のリクエストアトリビュートは、リクエストインターセプターが他のリクエストインターセプターやリクエストハンドラーにデータやエンティティを渡す方法を提供します。

AbstractRequestInterceptor クラスを使用する方法に従っている場合、次の方法でリクエストインターセプターを登録できます

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# Implement FooInterceptor, BarInterceptor, BazInterceptor classes

sb.add_global_request_interceptor(FooInterceptor())
sb.add_global_request_interceptor(BarInterceptor())
sb.add_global_request_interceptor(BazInterceptor())

global_request_interceptor デコレーターを使用する方法に従っている場合、インターセプター関数を明示的に登録する必要はありません。スキルビルダーインスタンスを使用してすでにデコレートされています。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# decorate foo_interceptor, bar_interceptor, baz_interceptor functions

上記の例では、SDKが以下の順序ですべてのリクエストインターセプターを実行します。

  1. FooInterceptor クラス / foo_interceptor 関数
  2. BarInterceptor クラス / bar_interceptor 関数
  3. BazInterceptor クラス / baz_interceptor 関数

応答インターセプター

グローバル応答インターセプターは、サポートされるリクエストハンドラーの処理後に、ハンドラー入力オブジェクト、つまり応答を受け付けて処理します。リクエストインターセプターと同様に、カスタム応答インターセプターも二通りの方法で実装できます。

  • AbstractResponseInterceptor クラスを実装する。
  • スキルビルダーglobal_response_interceptor デコレーターを使用してカスタム処理関数をデコレートする。

インターフェース

AbstractResponseInterceptorクラス

AbstractResponseInterceptor クラスを使用するには、処理メソッドを実装する必要があります。このメソッドはハンドラー入力インスタンスの、先に実行されたリクエストハンドラーから返された応答オブジェクトを取ります。このメソッドから返されるものはありません。

class AbstractResponseInterceptor(object):
    @abstractmethod
    def process(self, handler_input, response):
        # type: (HandlerInput, Response) -> None
        pass

以下は、正常に処理されたリクエストから受け取った応答を、Alexaサービスにその応答が返される前にAWS CloudWatchログに書き込むレスポンスインターセプタークラスの例です。

from ask_sdk_core.dispatch_components import AbstractResponseInterceptor

class LoggingResponseInterceptor(AbstractResponseInterceptor):
    def process(handler_input, response):
        print("Response generated: {}".format(response))
SkillBuilderのglobal_response_interceptorデコレーター

SkillBuilderクラスの global_response_interceptor デコレーターは、AbstractResponseInterceptor クラスに搭載されたカスタムラッパーであり、カスタムでデコレートされた任意の関数と同じ機能を提供します。ただし、デコレーターを使用するには以下の2点を考慮してください。

  • デコレーターはスキルビルダーインスタンスを必要とするため、インターセプターを登録するには、関数名としてではなく関数として呼び出される必要があります。
  • デコレートされた関数は2つのパラメーターを受け付けます。それぞれハンドラー入力オブジェクトおよび応答オブジェクトです。この関数から返される値はキャプチャーされません。
class SkillBuilder(object):
    ....
    def global_response_interceptor(self):
        def wrapper(process_func):
            # wrap the process_func into a class
            # add the class into response interceptors list
            ....
        return wrapper

以下は、応答インターセプターとして使用できるログ記録関数の例です。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

@sb.global_response_interceptor()
def response_logger(handler_input, response):
    print("Response generated: {}".format(response))

応答インターセプターの登録と処理

応答インターセプターは、受け取るリクエストのリクエストハンドラーが実行された直後に呼び出されます。

AbstractResponseInterceptor クラスを使用する方法に従っている場合、次の方法で応答インターセプターを登録できます

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# Implement FooInterceptor, BarInterceptor, BazInterceptor classes

sb.add_global_response_interceptor(FooInterceptor())
sb.add_global_response_interceptor(BarInterceptor())
sb.add_global_response_interceptor(BazInterceptor())

global_response_interceptor デコレーターを使用する方法に従っている場合、インターセプター関数を明示的に登録する必要はありません。スキルビルダーインスタンスを使用してすでにデコレートされています。

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# decorate foo_interceptor, bar_interceptor, baz_interceptor functions

リクエストインターセプターの処理と同様に、応答インターセプターはすべて、登録順に実行されます。