AWS API GatewayでContent-Type:application/x-www-form-urlencoded のPOSTデータを受け取り JSONに変換する


## やりたいこと
 AWS API GatewayでContent-Type:application/x-www-form-urlencoded のPOSTデータを受け取り JSONに変換する



## なんでやりたいの？
AWSのAPI Gateway を使用する場合、POST の Content-Typeは application/json が標準となっています。

また、Lambda の入力も json形式だといろいろと扱いやすくできています。

そのため、Lambda連携をする場合は特に、application/x-www-form-urlencoded のデータを受け取る場合には API Gateway でJSONにマッピングすると便利です。



## Lambdaの設定

[前提] NodeJSを使用。Configurationは公式のサンプルと同様

Codeは以下を入力してください

```
exports.handler = function(event, context) {
    console.log(JSON.stringify({event: event, context: context}, null, 2));
    context.succeed({event: event});
};
```



## API Gatewayの設定

[前提] API Gateway と Lambda の連携はできているものとします

1. Integration Request をクリック
2. Mapping Templates をクリック
3. + Add mapping template をクリック
4. Content-Type に application/x-www-form-urlencoded を入力
5. application/x-www-form-urlencoded をクリック
6. Input passthrough の右側の鉛筆アイコンをクリック
7. 選択ボックスから Mapping templateを選択し以下のコードをテキストエリアにペーストし、チェックマークをクリックしてSave.

```
## convert HTML POST data or HTTP GET query string to JSON

## get the raw post data from the AWS built-in variable and give it a nicer name
#if ($context.httpMethod == "POST")
 #set($rawAPIData = $input.path('$'))
 ## escape any quotes
 #set($rawAPIData = $rawAPIData.replace('"', '\"'))
#elseif ($context.httpMethod == "GET")
 #set($rawAPIData = $input.params().querystring)
 #set($rawAPIData = $rawAPIData.toString())
 #set($rawAPIDataLength = $rawAPIData.length() - 1)
 #set($rawAPIData = $rawAPIData.substring(1, $rawAPIDataLength))
 #set($rawAPIData = $rawAPIData.replace(", ", "&"))
#else
 #set($rawAPIData = "")
#end

## first we get the number of "&" in the string, this tells us if there is more than one key value pair
#set($countAmpersands = $rawAPIData.length() - $rawAPIData.replace("&", "").length())

## if there are no "&" at all then we have only one key value pair.
## we append an ampersand to the string so that we can tokenise it the same way as multiple kv pairs.
## the "empty" kv pair to the right of the ampersand will be ignored anyway.
#if ($countAmpersands == 0)
 #set($rawPostData = $rawAPIData + "&")
#end

## now we tokenise using the ampersand(s)
#set($tokenisedAmpersand = $rawAPIData.split("&"))

## we set up a variable to hold the valid key value pairs
#set($tokenisedEquals = [])

## now we set up a loop to find the valid key value pairs, which must contain only one "="
#foreach( $kvPair in $tokenisedAmpersand )
 #set($countEquals = $kvPair.length() - $kvPair.replace("=", "").length())
 #if ($countEquals == 1)
  #set($kvTokenised = $kvPair.split("="))
  #if ($kvTokenised[0].length() > 0)
   ## we found a valid key value pair. add it to the list.
   #set($devNull = $tokenisedEquals.add($kvPair))
  #end
 #end
#end

## next we set up our loop inside the output structure "{" and "}"
{
#foreach( $kvPair in $tokenisedEquals )
  ## finally we output the JSON for this pair and append a comma if this isn't the last pair
  #set($kvTokenised = $kvPair.split("="))
  #if($kvTokenised.size() == 2 && $kvTokenised[1].length() > 0)
    #set($kvValue = $kvTokenised[1])
  #else
    #set($kvValue = "")
  #end
  #if( $foreach.hasNext )
    #set($itemDelimiter = ",")
  #else
    #set($itemDelimiter = "")
  #end
 "$kvTokenised[0]" : "$kvValue"$itemDelimiter
#end
}
```



## 確認

1. APIをDeploy
2. ターミナル等からCurlでRequestを投げる

```
curl -H "Content-Type: application/x-www-form-urlencoded" -X POST -d "abc=xyz&def[0]=zzz&def[1]=xxx%0ayyy" [API GatewayのInvoke URL]
```

以下の Responseが返ってきたら成功

```
{"event":{"abc":"xyz","def[0]":"zzz","def[1]":"xxx%0ayyy"}}
```

もし、以下の Responseが返ってきた場合は URLが違うことが多いので見直すこと

```
{"message":"Missing Authentication Token"}
```

Content-Type等が違う場合は以下の Responseが返ってくる

```
{"message": "Could not parse request body into json: Unrecognized token \'abc\': was expecting (\'true\', \'false\' or \'null\')\n at [Source: [<<some>>; line: 1, column: 5]"}
```



## 参考

* [HOWTO: Mapping Template v3.0 to convert form POST data or GET query to JSON](https://forums.aws.amazon.com/thread.jspa?messageID=673012&tstart=0#673012)
* [HTMLフォームからAPIGatewayを使ってAWS LambdaにPOSTする](http://qiita.com/satetsu888/items/40fc387735192b794da8)
* [API Gatewayへの入力値にLambdaからアクセスする](http://qiita.com/r7kamura/items/6420538789da95cd2f47)