AWS Lambda のChalice使ってflask APIをデプロイする
前回、Netlifyの記事を書きましたが、今回は、Chaliceです。
接続としては、以下の通りです。
<LINE> ー <Dialogflow> ー <heroku> ー <VPS>
今回は、herokuのところをサーバレスに置き換えたいと思います。はっきりって、仲介をするだけなので処理自体はそんなに難しくありません。コストパフォーマンスを考えるとサーバレスが向いているんじゃないかなと思った次第です。
結論としては上手く入れ替えることができました。web開発者ではないpythonエンジニアにとって、簡単にAPIがデプロイできることは非常にありがたいことでした。
Chaliceとは
https://aws.github.io/chalice/index.html
AWSのLambdaにflask APIをデプロイする方法を調べていると、実に色々な方法がありますが、Chaliceを選択した理由は以下の通りです。
- AWS純正
- flaskアプリケーションと類似の構造
元のプログラムがflaskでしたので、これをベースに非常に簡単に移植できました。
また、古い情報では、API Gatewayの設定が面倒など書かれていますが、現時点では特に面倒なことはありませんでした。
実際の手順は、以下のサイトに丁寧に記載されています。
Flaskからの変更点
flaskのコードからの変更点は、以下の通りです。プログラムの構造はほぼ同じなので、変更箇所はごく限られています。
from chalice import Chalice //追加 flask関係のimportは削除
import requests // ※ requirement.txtが自動生成されているので「requests」を追記する
import json
import random
from chalicelib.reply import reply_message // 自作ライブラリの位置をchalicelibフォルダ以下に変更
app = Chalice(app_name='dialogflow-webhook') // 自動生成
@app.route('/')
def index():
return {'hello': 'world'}
@app.route('/echo', methods=['POST'],
content_types=['application/json'])
def echo():
request = app.current_request
user_message = request.json_body['queryResult']['queryText'] // POSTメッセージの取得方法変更
# 応答生成サーバに対してuser_messageを送り、複数の応答候補を受け取る
res_message = reply_message(user_message)
# 応答候補からランダムに一つを選択
i = random.randint(0,len(res_message)-1)
# Dialogflowの書式に合わせて、応答jsonを設定
reply = {"fulfillmentText": res_message[i],}
return reply
テスト
chalice deploy を実行すると、Lambda関数とAPI Gatewayが生成されます。
今回のテストは、API Gatewayから行いました。
サイドメニューのリソースを選択して、テスト対象のメソッドを選ぶと、クライアント上に「テスト」という文字が見えます。
これをクリックすると、ヘッダやリクエスト本文を指定してメソッドのテストを実行することができます。
問題が発生したときには、Lambdaの「モニタリング」から「ログ」を確認することができるので参考になるかと思います。
トラブル調査
以下、つまづきポイントです。
app.current_request.json_body
chaliceの公式REST APIチュートリアルのRequest Metadataの項目にPOSTデータの取得方法として、app.current_requestを使うとあります。JSONの本文は、current_request.json_bodyとありますが、ここにうまく値がセットされず、nullで返ってきます。
しばらく悩んだ後に、その下のRequest Content Typesを読むと、
When a request is made with a
Content-Type
ofapplication/json
, theapp.current_request.json_body
attribute is automatically set for you. This value is the parsed JSON body.
とありました。
ヘッダに Content-Type:application/json を追加することで、無事にjson_bodyを取得することができました。
Dialogflowから送られてきたユーザメッセージは、以下のように取得できます。
request = app.current_request
user_message = request.json_body['queryResult']['queryText']
ドキュメントは、必要なところだけ拾い読みしがちですが、きちんと読まないとダメですね。(反省)
おまけ (イージーミス)
先述の通り、Dialogflowにつなぐ前に、API Gatewayのメソッドテスト画面から、テストを実施しました。
ヘッダにContent-Type:application/jsonを追加し、リクエスト本文には、Dialogflowのデータを取得して貼り付けて、想定通りの動作を確認しています。
ところが、Dialogflowにつないでみると、うまく動きません。
またまたしばらく悩みましたが、Dialogflowのwebhookを設定する画面にHEADERSという項目があるのに気づきました。テスト時にはちゃんとヘッダを設定していたのに本番環境でリクエスト側の設定を忘れていたのです。
Content-Type:application/jsonを設定することで、無事にherokuとの入れ替えを完了しました。