Python Serverless Microframework for AWS (以下、chalice) を利用して
Content-Type: application/x-www-form-urlencoded
のPOSTを受けてみたことを簡単にまとめてみました。
chalice は特に指定を行わない場合は application/json で受けるのがデフォルトっぽいですが、やっぱり application/x-www-form-urlencoded を扱いたいこともあるわけなので。
ドキュメントをみると
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になっており最新バージョンで再度試してみます
前回と同様にしてchaiceでデプロイを行うと、最新バージョンではちゃんとAPI Gatewayのマッピングテンプレートにapplication/x-www-form-urlencoded 用のテンプレートが指定されていました
やっと動いたよ!と思うんですが・・・いろいろやっている間に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/
この機能を使えばContent-Typeを気にせずにLamda関数にリクエストを送れるので、今までよりだいぶお手軽な気がしますし、Lambdaのblueprintから新規作成したりすると、すでにこの機能を使ったANYでProxyを利用した接続になっています。
実際にchalice でapplication/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 usedapp.current_request.raw_body
to access the raw body bytes:
Chalice — Python Serverless Microframework for AWS 0.2.0 documentation
http://chalice.readthedocs.io/en/latest/api.html?highlight=current_request#request
実際に試してみると
states=WA&states2=CA
のような形で、パラメータ文字列を取得することができます。また、こちらはJSON形式でリクエストを行うとマッピングテンプレートがありませんので当然ながら
{
"message": "Unsupported Media Type"
}
となります。
まとめ
API GatewayのアップデートによりProxy機能で簡単にLambda関数を呼び出して実行できるようになっています。
一方で、chaliceはターミナル上から手軽にデプロイと確認が行え、ログに関しても見ることができます。
API Gatewayの新機能での実現とchaliceでの実現、どちらでもJSON以外のPOSTは簡単に受けられました。chaliceは今後もこの手軽な感じで進んで行ってもらいたいです。
以上。