Python Serverless Microframework for AWS (chalice) でJSON 以外のPOST を受けてみた #aws #chalice #lambda

Python Serverless Microframework for AWS (以下、chalice)  を利用して

Content-Type: application/x-www-form-urlencoded

のPOSTを受けてみたことを簡単にまとめてみました。

chalice は特に指定を行わない場合は application/json で受けるのがデフォルトっぽいですが、やっぱり application/x-www-form-urlencoded を扱いたいこともあるわけなので。

ドキュメントをみると

github.com 

Quickstart and Tutorial — Python Serverless Microframework for AWS 0.2.0 documentation

と、丁寧に書かれているのでそのままコピペするも実行結果は

{
    "message": "Unsupported Media Type"

API Gateway の設定がどうなっているか見てみると・・・マッピングテンプレートで application/x-www-form-urlencoded  用のテンプレートが指定されておらず  application/json のみで、どんな場合もパススルーしない設定になっているというね・・・

と、ここまではちょっと前までの  バージョン0.1.0 での話ですが、ドキュメントに書いてあって実現できなかったのはちょっと残念でした。

バージョン0.2.0

で、現在のバージョンは0.2.0になっており最新バージョンで再度試してみます

github.com

前回と同様にしてchaiceでデプロイを行うと、最新バージョンではちゃんとAPI Gatewayマッピングテンプレートにapplication/x-www-form-urlencoded  用のテンプレートが指定されていました

f:id:uchimanajet7:20161003153221p:plain

やっと動いたよ!と思うんですが・・・いろいろやっている間にAPI Gateway側のアップデートがあり、「Lambda プロキシ統合の使用」といったリクエストをLambdaに簡単にスルーできるオプションが追加になりました

API Gateway のアップデート – API 開発を簡素化する新機能 | Amazon Web Services ブログ

https://aws.amazon.com/jp/blogs/news/api-gateway-update-new-features-simplify-api-development/

dev.classmethod.jp

この機能を使えばContent-Typeを気にせずにLamda関数にリクエストを送れるので、今までよりだいぶお手軽な気がしますし、Lambdaのblueprintから新規作成したりすると、すでにこの機能を使ったANYでProxyを利用した接続になっています。

実際にchaliceapplication/x-www-form-urlencoded  用のテンプレートが用意された状態と、このProxy機能でスルーされた状態でLambdaに届くデータの内容を比較してみました。

送信したのは上記したchaiceのドキュメントに書かれている httpie を利用した例と同じで

http --form POST https://endpoint-hoge1.ap-northeast-1.amazonaws.com/dev/ states=WA states2=CA --debug

Proxyの場合はevent 引数に以下のような感じで入ってくる

{u'body': u'states=WA&states2=CA', u'resource': u'/proxy-test', u'requestContext': {u'resourceId': u'co8iwy', u'apiId': u'1m6791x3wk', u'resourcePath': u'/proxy-test', u'httpMethod': u'POST', u'requestId': u'6f00523f-8936-11e6-831a-e9eb40e256a5', u'accountId': u'316874585308', u'identity': {u'apiKey': None, u'userArn': None, u'cognitoAuthenticationType': None, u'caller': None, u'userAgent': u'HTTPie/0.9.6', u'user': None, u'cognitoIdentityPoolId': None, u'cognitoIdentityId': None, u'cognitoAuthenticationProvider': None, u'sourceIp': u'xxx.xxx.xxx.xxx', u'accountId': None}, u'stage': u'prod'}, u'queryStringParameters': None, u'httpMethod': u'POST', u'pathParameters': None, u'headers': {u'Content-Type': u'application/x-www-form-urlencoded; charset=utf-8', u'Via': u'1.1 064b8001bd91f53f9b5f04fba4435677.cloudfront.net (CloudFront)', u'Accept-Encoding': u'gzip, deflate', u'CloudFront-Is-SmartTV-Viewer': u'false', u'CloudFront-Forwarded-Proto': u'https', u'X-Forwarded-For': u'xxx.xxx.xxx.xxx', u'CloudFront-Viewer-Country': u'JP', u'Accept': u'*/*', u'User-Agent': u'HTTPie/0.9.6', u'Host': u'1m6791x3wk.execute-api.ap-northeast-1.amazonaws.com', u'X-Forwarded-Proto': u'https', u'X-Amz-Cf-Id': u'4meZj8EGU3Ui_FvMztZ-Y5Oh4pkz3Tm-s4wKSd1VaZ9o722piksxIA==', u'CloudFront-Is-Tablet-Viewer': u'false', u'X-Forwarded-Port': u'443', u'CloudFront-Is-Mobile-Viewer': u'false', u'CloudFront-Is-Desktop-Viewer': u'true'}, u'stageVariables': None, u'path': u'/proxy-test'} 

'Content-Type':  'application/x-www-form-urlencoded; charset=utf-8' のPOSTを受けて 'body':  'states=WA&states2=CA' にパラメータ文字列が入っているのがわかります

当然パススルーしてるだけなので他のContent-Typeでも問題なしなので

{u'body': u'{"states": "WA", "states2": "CA"}', u'resource': u'/proxy-test', u'requestContext': {u'resourceId': u'co8iwy', u'apiId': u'1m6791x3wk', u'resourcePath': u'/proxy-test', u'httpMethod': u'POST', u'requestId': u'a407c1a9-8941-11e6-a603-f18eaa90784e', u'accountId': u'316874585308', u'identity': {u'apiKey': None, u'userArn': None, u'cognitoAuthenticationType': None, u'caller': None, u'userAgent': u'HTTPie/0.9.6', u'user': None, u'cognitoIdentityPoolId': None, u'cognitoIdentityId': None, u'cognitoAuthenticationProvider': None, u'sourceIp': u'xxx.xxx.xxx.xxx', u'accountId': None}, u'stage': u'prod'}, u'queryStringParameters': None, u'httpMethod': u'POST', u'pathParameters': None, u'headers': {u'Content-Type': u'application/json', u'Via': u'1.1 fbd80349eadc2efc6c27a91d8ce7b321.cloudfront.net (CloudFront)', u'Accept-Encoding': u'gzip, deflate', u'CloudFront-Is-SmartTV-Viewer': u'false', u'CloudFront-Forwarded-Proto': u'https', u'X-Forwarded-For': u'xxx.xxx.xxx.xxx', u'CloudFront-Viewer-Country': u'JP', u'Accept': u'application/json, */*', u'User-Agent': u'HTTPie/0.9.6', u'Host': u'1m6791x3wk.execute-api.ap-northeast-1.amazonaws.com', u'X-Forwarded-Proto': u'https', u'X-Amz-Cf-Id': u'QYZPvi7XzpkyeWG6FUIGPvilp3mRp4uE-bw6pk8D-5jFcR2IrrlH5g==', u'CloudFront-Is-Tablet-Viewer': u'false', u'X-Forwarded-Port': u'443', u'CloudFront-Is-Mobile-Viewer': u'false', u'CloudFront-Is-Desktop-Viewer': u'true'}, u'stageVariables': None, u'path': u'/proxy-test'}

'Content-Type': 'application/json' で受けて 'body':  '{"states": "WA", "states2": "CA"}' とJSON文字列が入っているのがわかります。

Lambdaコード側で Content-Type によって処理を分ける場合があれば便利かもしれないですね

chalice の場合はドキュメントに書いてある通り、JSONの場合には current_request.json_body に値が、それ以外の場合は current_request.raw_body に値が入っているそうです

The second thing worth noting is that app.current_request.json_body is only available for the application/json content type. In our example above, we used app.current_request.raw_body to access the raw body bytes:

ChalicePython Serverless Microframework for AWS 0.2.0 documentation

http://chalice.readthedocs.io/en/latest/api.html?highlight=current_request#request

github.com

実際に試してみると

states=WA&states2=CA

のような形で、パラメータ文字列を取得することができます。また、こちらはJSON形式でリクエストを行うとマッピングテンプレートがありませんので当然ながら

{
    "message": "Unsupported Media Type"

となります。

まとめ 

API GatewayのアップデートによりProxy機能で簡単にLambda関数を呼び出して実行できるようになっています。

一方で、chaliceはターミナル上から手軽にデプロイと確認が行え、ログに関しても見ることができます。

API Gatewayの新機能での実現とchaliceでの実現、どちらでもJSON以外のPOSTは簡単に受けられました。chaliceは今後もこの手軽な感じで進んで行ってもらいたいです。

 

以上。