LoginSignup
21
19

More than 5 years have passed since last update.

【AWS】API GatewayのMapping Templateで、Key=ValueペアをJSONに変換する

Last updated at Posted at 2016-10-22

はじめに

API Gatewayでapplication/x-www-form-urlencoded形式のPOSTリクエストを受け取るための方法を説明します。
これは、HTTPのPOSTリクエストのBodyにGETパラメータのような形式の文字列(Key1=Value1&Key2=Value2...)が指定される形式となります。このようなリクエストをapplication/json形式に変換するためのTemplateが必要となったのでまとめました。

なお、下記の記事からの派生となります。TwilioからのPOSTリクエストをJSONに変換するために必要でした。
仕組みから理解するTwilio #3-1 - AWS API Gateway+Lambda実装Walkthrough(前編)

Mapping Templateとは

API Gatewayで、データ形式を変換するための仕組み。Velocity Template Language(以下、VTL)とJSONPathを利用して定義します。

Amazon API Gateway API リクエストとレスポンスペイロードのマッピングを設定する
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/models-mappings.html

Apache Velocity Engine - User Guide
http://velocity.apache.org/engine/devel/user-guide.html

Apache Velocity Engine VTL Reference
http://velocity.apache.org/engine/devel/vtl-reference.html

JSONPath - XPath for JSON
http://goessner.net/articles/JsonPath/

やりたいこと

application/x-www-form-urlencoded形式のPOSTリクエストをJSONに変換することが目的です。
POSTメソッドで送信されたHTTPリクエストBodyに「&」で区切られたKey=Valueペア(改行なし)が設定されるので、これをJSONに変換することとなります。
Valueが設定されていないKeyは無視します。

Input
ApiVersion=2010-04-01&ApplicationSid=AP75zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz&Called=&Caller=client%3ADeviceId_0001&From=client%3ADeviceId_0001&To=&callerPhoneNumber=%2B81-50-3123-4567&callOutgoingPhoneNumber=%2B81-90-5987-6543
Output
{
    "From": "client:DeviceId_0001",
    "Caller": "client:DeviceId_0001",
    "ApiVersion": "2010-04-01",
    "ApplicationSid": "AP75zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
    "callerPhoneNumber": "+81-50-3123-4567",
    "callOutgoingPhoneNumber": "+81-90-5987-6543"
}

「Called」はKeyのみでValueが存在しないため、変換後のJSONには存在していません。

Templateの実装

Twilio特化型

#set($raw_input = $input.path("$"))

{
  #foreach( $kvpairs in $raw_input.split( '&' ) )
    #set( $kvpair = $kvpairs.split( '=' ) )
      #if($kvpair.length == 2)
        "$kvpair[0]" : "$kvpair[1]"#if( $foreach.hasNext ),#end
      #end
  #end
}

リクエストBodyに設定された文字列を「&」で分割したのち、それらを更に「=」で分割してKeyとValueの値を取り出し、JSONオブジェクトのペアとして出力する、ということを行っています。
ポイントは、Key=Valueを「=」で分割した後に、要素数をチェックしているところです。要素数が2の場合のみ、変換後のJSONに出力します。それ以外の要素数に分割された場合は不正とみなし無視します。

汎用型

GETクエリパラメータにも対応した汎用型のテンプレートは下記になります。AWS Developer Forumで議論され、まとめてられていた内容に若干手を加えたものです。
下記の記事で最終的にまとめられていたものは、Keyの指定があるがValueが設定されていないケース(「Key2=&Key3=Value3」のKey2ようなケース)に対応していなかったので修正しました。

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#725034

## 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("$"))
#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.size() == 2) && ($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("="))
 "$util.urlDecode($kvTokenised[0])" : #if($kvTokenised.size() > 1 && $kvTokenised[1].length() > 0)"$util.urlDecode($kvTokenised[1])"#{else}""#end#if( $foreach.hasNext ),#end
#end
}
21
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
19