リクエスト処理



リクエスト処理

標準のリクエスト

作成したスキルサービスとAlexaとの通信は、SSL/TLSを利用してHTTPを使用するリクエスト-応答メカニズムで行います。ユーザーがAlexaスキルと対話するとき、作成したサービスは、JSON本文を含むPOSTリクエストを受け取ります。このリクエスト本文には、サービスがロジックを実行してJSON形式の応答を生成するために必要なパラメーターが含まれています。リクエスト本文のJSON構造についてのドキュメントは、こちらを参照してください。

PythonはJSONをdictオブジェクトとしてネイティブに処理できますが、型をサポートするため、スキルで利用するにはモデルオブジェクト(ask-sdk-modelパッケージ)に逆シリアル化されます。

ハンドラー入力

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

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

リクエストハンドラー

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

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

インターフェース

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

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

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

サンプルコード

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

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

class HelloWorldIntentHandler(AbstractRequestHandler):
    def can_handle(self, handler_input):
        return is_intent_name("HelloWorldIntent")(handler_input)

    def handle(self, handler_input):
        speech_text = "こんにちは";

        return handler_input.response_builder.speak(speech_text).set_card(
            SimpleCard("ハローワールド", speech_text)).response

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

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 = "こんにちは"

    return handler_input.response_builder.speak(speech_text).set_card(
        SimpleCard("ハローワールド", 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()

# FooHandler、BarHandler、BazHandlerクラスを実装します

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()

# foo_handler、bar_handler、baz_handler関数をデコレートします

例外ハンドラー

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

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

リクエストハンドラーと同様に、例外ハンドラーも2とおりの方法で実装できます。

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

インターフェース

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

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):
            # can_handleとhandleをクラスにラップします
            # クラスを例外ハンドラーリストに追加します
            ....
        return wrapper

サンプルコード

以下は、名前に「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 = "すみません、何をしたらいいのかわかりません。後でもう一度お試しください。"

        return handler_input.response_builder.speak(speech_text).response

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

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

@sb.exception_handler(can_handle_func = lambda i, e: 'AskSdk' in e.__class__.__name__)
def ask_exception_intent_handler(handler_input, exception):
    speech_text = "すみません、何をしたらいいのかわかりません。後でもう一度お試しください。"

    return handler_input.response_builder.speak(speech_text).response

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

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# FooExceptionHandler、BarExceptionHandler、BazExceptionHandlerクラスを実装します

sb.add_exception_handler(FooExceptionHandler())
sb.add_exception_handler(BarExceptionHandler())
sb.add_exception_handler(BazExceptionHandler())
from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# foo_exception_handler、bar_exception_handler、baz_exception_handler関数をデコレートします

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

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

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

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

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

インターフェース

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

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

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

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

サンプルコード

以下は、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))
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))

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

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

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# FooInterceptor、BarInterceptor、BazInterceptorクラスを実装します

sb.add_global_request_interceptor(FooInterceptor())
sb.add_global_request_interceptor(BarInterceptor())
sb.add_global_request_interceptor(BazInterceptor())
from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()
# foo_interceptor、bar_interceptor、baz_interceptor関数をデコレートします

応答インターセプター

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

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

インターフェース

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

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

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

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

サンプルコード

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

from ask_sdk_core.dispatch_components import AbstractResponseInterceptor

class LoggingResponseInterceptor(AbstractResponseInterceptor):
    def process(handler_input, response):
        print("Response generated: {}".format(response))
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))

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

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

from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# FooInterceptor、BarInterceptor、BazInterceptorクラスを実装します

sb.add_global_response_interceptor(FooInterceptor())
sb.add_global_response_interceptor(BarInterceptor())
sb.add_global_response_interceptor(BazInterceptor())
from ask_sdk_core.skill_builder import SkillBuilder

sb = SkillBuilder()

# foo_interceptor、bar_interceptor、baz_interceptor関数をデコレートします