Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

はじめに

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
}
tmiki
Tech Fun株式会社( http://www.techfun.co.jp/ )所属のエンジニア。10年ぐらいインフラエンジニア、ここ数年はアプリエンジニア。 好きな言語はDartとゲール語とサンスクリット語、好きなAWSサービスはIAM/STSです。 本サイトで投稿する内容は個人の見解に基づくものであり、所属組織の意向に関わるものではありません。
techfun
Tech FunはITの力で世界を豊かにする総合サービス企業です。 IT研修スクール「TechFun.jp(https://techfun.jp/)」、eラーニングプラットフォーム「StudySmile(https://studysmile.com/)」のほか、ミャンマーオフショア開発、スマートフォンアプリ開発、Webシステム開発、SIサービスを展開しています。
https://www.techfun.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away