2021年4月30日金曜日

【AWS】API Gatewayで超手軽にMockを作成する。

オフィス狛 技術部のyuckieeeです。

Androidアプリの開発中に動作確認をしようとしたとき、データ取得先のAPIが未完成だったり、特定のレスポンスやエラー(メンテナンス画面にするなど)を返すように制御するのが難しい...
など、もやもやすることがありました。

で、、それならシミュレータとかMockとか呼ばれてるものを自分で用意しちゃえばいいじゃん!?
と、思い立ち、AWSのAPI Gatewayが提供する機能を使用して作成してみました。
これが意外と使える子だったので、今回は一番シンプルに使える「統合Mock」というサービスの使い方を紹介します!

Step1:API GatewayにAPIを登録する

何はともあれ、APIの外枠を作らないことには始まりません。
AWSの管理コンソールからAPI Gateway画面を開き[API作成]をクリックして、APIを作成していきます!

最初に、作成するAPIタイプの選択画面が表示されますので"REST API"の[構築]を選択してください。

次にAPIの作成画面が表示されますので適当にAPI名を指定してください。
この値はアクセスURLに反映はされませんが、変更が出来ないようなので、ご注意ください。
これでAPIの新規作成は完了です。

Step2:リソースとメソッドを作成する

APIの作成が完了するとリソース管理画面に遷移します。
この時点では、下図のとおり何も設定されていませんので、ここから設定を追加していきます。
作成開始前にイメージ共有しておいた方が分かりやすいと思うので、まずはMock化したいAPIの概要を決めておきます。
今回は以下のAPIレスポンスを代替する統合Mockを作ろうと思います。

 GET / https://www.yuckieee.com/book/search


上記APIの下線部がAWS Gatewayが自動発行するパスで置き換わる部分です。
そのため、book以降のパス部分を作成対象として進めていきます。

まずは[アクション]メニューから[リソースの作成]を選択し、上記に沿ってリソースを定義します。
ここで"リソース"と言っているのがアクセスするURLのパスとなります。
リソースは、ディレクトリ 構造での管理となっており、最上位を/(ルート)リソースと呼びます。
このルートリソースに子リソースを追加していく形でリソースを作成していきます。
今回は
/(ルート)リソースにbook子リソースを追加
bookリソースにsearch子リソースを追加
の順でリソースを作成します。

リソースの作成後、レスポンス返却させたいリソース(=今回はsearchリソース)を選択し、[アクション]メニューから[メソッドの作成]を選択します。
[メソッドの作成]クリック後、下図のように作成したいHTTPメソッドが選択可能になるため、今回は"GET"を選択し、右側の確定チェックボタンをクリックします。
確定チェッククリック後、メソッドのセットアップ画面が表示されます。
今回はAWSの統合Mock機能を使用するため、統合タイプから"Mock"を選択し[保存]をクリックします。
これでリソースとメソッドの作成は完了です。

Step3:マッピングテーブルを定義する

統合Mockが作成されると、下図のような定義画面が表示されます。

それぞれ、私視点での概要を軽く説明すると下記のとおりです。

タイプ 説明
メソッド
リクエスト
実際に受信したAPIリクエストに対して、入力値チェックやAPIキー指定などの設定が可能です。
統合
リクエスト
統合Mockエンドポイントに引き渡す値の設定を行います。
統合Mockの場合、ここで設定された値が統合レスポンスに引き渡されます。
統合
レスポンス
統合Mockエンドポイント(統合リクエスト)から受け取った値を元にレスポンスを定義します。ここでHTTPステータスやヘッダ、レスポンスBodyなどを設定します。
メソッド
レスポンス
統合Mockの場合、レスポンスに必要なHTTPステータスをここで設定しておく必要があるようです。

色々触ってみながら試していただくのが良いですが、 最もシンプルに使いたい場合は、統合レスポンスのマッピングテーブルを作成するだけ出来ちゃいます!

ということで、[統合レスポンス]を開きます。
統合レスポンスでは、統合リクエストから受信するHTTPステータスごとにマッピングテンプレートの設定が可能です。 新規作成段階では、統合レスポンスがデフォルトで1つ用意されており、統合リクエストで指定されたHTTPステータスに対応する統合レスポンスが存在しない場合、 この統合レスポンスのマッピングテンプレートがレスポンスされます。
※ただし、API Gateway自体がエラーとなった場合を除きます。

今回マッピングテンプレートに指定したい内容は以下のとおりです。
[ { "id": "1001", "title": "オフィス狛ものがたり", "Author": "yuckieee" }, { "id": "1002", "title": "狛ちゃん冒険譚", "Author": "yuckieee" } ]

デフォルトの統合レスポンスを順番に展開し、マッピングテンプレートのContent-Type:application/jsonにレスポンスしたい内容を入力します。
入力完了後、右下の[保存]をクリックして保存します。

 👉  統合リクエストと統合レスポンスのマッピングテンプレートの記述方法は
   「おまけ:リクエストによってレスポンス内容を切り替える」の章を参照してください

マッピングテンプレートの設定は以上で完了です。

Step4:動作確認(テスト)を行う

これまでの設定が問題ないか、デプロイまでにテストできる機能がありますので、これを使って動作確認してみます。 リソースの管理画面左にある[テスト⚡️]をクリックしてください。
メソッドのテスト画面が表示されますので、画面中央下部の[⚡️テスト]クリックで、先ほど定義した内容でAPIが呼び出されます。
[⚡️テスト]クリック後、結果が画面右側に表示されます。
この内容が実際にMockにアクセスした際に返却される値と同様の内容となっていますので、この段階でレスポンスに問題がないかを確認します。
もし、Internal Errorなどが返却されている場合は、設定をもう一度見直すか、ログ領域を参照しエラー内容を確認してください。

Step5:デプロイ(外部公開)する

テストで動作に問題がないことを確認したあとは、実際にデプロイしてみます。
[アクション]メニューの[APIのデプロイ]をクリックしてください。
APIのデプロイダイアログが表示されますので、デプロイするステージを新規作成します。
デプロイステージ名は、URLにも組み込まれますので分かりやすい名前にしておきましょう。
今回はdevとしておきます。
問題なくデプロイが完了すると、新規作成したステージのエディター画面に遷移します。 遷移先の画面に呼び出し用のURLが記載されていますので、このURLを使用してMockにアクセスが可能になります。

Step6:実行確認する

最後にPOSTMANを使用してリクエストが可能かを確認してみます。
POSTMANを起動し、該当のURLを指定して実行してみてください。下図のようにマッピングテンプレートに定義した値が想定通り返却されればOKです!
※APIクライアントツールが入っていない場合は、GETであればブラウザ直接指定でも確認可能です!
デプロイが出来ていない場合、下図のようにMissing Authentication Tokenエラーが返ってきますので再度デプロイしてみてください。
これでMockとして使用できるようになりました!
あとは参照元のAndroidアプリ側URLをMockに切り替えてLet's テスト〜♪♬♩

おまけ:リクエストによってレスポンス内容を切り替える

テストをしていると、HTTPステータスやレスポンスBODYの値をリクエストによって切り替えたくなりますよね。私もそうでした!!!!(最終的にはLambdaを使用しましたが)
簡単な切り替えであれば、統合Mock機能でも可能です。幾つか切り替え方法をご紹介しますので参考にしてみてください。

💡 マッピングテンプレート記述言語

マッピングテンプレートではApache Velocity Template Language (VTL)という言語が採用されているようです。 この言語仕様に沿って変数の定義やIF文、FOR文などの記述が可能です。
今回は、これらを使ってリクエストによるレスポンス内容の切り替えを行っていきたいと思います。
なお、ここでは言語自体の説明を省きますので、詳細は公式リファレンスを参照ください。

パターン1:HTTPステータスコードを切り替える

① 統合リクエストのマッピングテンプレート編集
HTTPステータスを切り替えたい場合は、統合リクエストのマッピングテンプレートを編集します。
デフォルトでは{statusCode: 200}が指定されていますが、リクエスト値によって切り替えたい場合は以下の要領で設定します。

👉 GETメソッド(クエリ文字使用)の場合
クエリ文字に"id"というパラメータを指定していたと仮定して記述しています。
例:/book/search?id=1001
#set($id = $input.params("id")) #if($id == "1001") { "statusCode": 200 } #elseif($id == "1002") { "statusCode": 400 } #else { "statusCode": 500 } #end

👉 POSTメソッド(リクエストBODY使用)の場合
リクエストBODYにidというパラメータを指定していたと仮定して記述しています。
例:{ "id": "1001" }
#set($id = $util.parseJson($input.body).id) #if( $id == "1001") { "statusCode": 200 } #elseif($id == "1002") { "statusCode": 400 } #else { "statusCode": 500 } #end

② メソッドレスポンスの追加
レスポンスのHTTPステータスにバリエーションを持たせたい場合、最初にメソッドレスポンスを追加する必要があります。メソッドレスポンス画面を開き[➕ レスポンスの追加]をクリックし、追加したいHTTPステータスコードを入力し、確定してください。
今回は400と500を追加してみました。
③ 統合レスポンスの追加
続いて、HTTPステータスごとの統合レスポンスを作成します。
デフォルトをHTTPステータス200で使用することにして、新しくHTTPステータス400と500用の統合レスポンスを作成します。統合レスポンス画面を開き[➕ 統合レスポンスの追加]をクリックして下記の要領で統合レスポンスを追加してください。

HTTPステータスの正規表現:
 統合リクエストから引き渡されるStatusCodeのうち、対象とする値を指定します。
 この値は正規表現での指定も可能なため、4\d{2}と書くことでステータスコード400番台全てを対象とすることも出来ます。

メソッドレスポンスのステータス:
 ②で作成したメソッドレスポンスを指定します。
 ※先にメソッドレスポンスを作成したのは、ここで指定させるためでした。

コンテンツの処理:
 デフォルト値であるパススルーのままで!
この段階で一度[保存]をして開き直すと、マッピングテンプレートの追加が可能になりますので、適当なレスポンス内容を設定してみてください。
その後、HTTPステータス500についても同様に設定します。
全ての設定が完了したあとに、再度STEP4~5で紹介した手順を実施いただけば完了です...!

【補足】動作確認時のクエリ文字/リクエストBODYの指定について
リソースに指定されたHTTPメソッドがGETなどクエリ文字指定可能なものである場合、メソッドテスト画面にクエリ文字の入力エリアが表示されます。
同様にPOSTなど、リクエストBODY指定可能なメソッドの場合は、リクエスト本文の入力エリアが表示されます。(注:リクエスト本文は、GETメソッドの場合は非表示となります)

パターン2:レスポンスBODYを切り替える

HTTPステータスは200のままで、リクエスト内容によってレスポンス内容を変えたい!!!
というパターンのご紹介です。
なお、クエリ文字を使用する場合は、パターン1のHTTPステータスでご紹介した$input.params("id")が、統合レスポンスでも使用できるため説明を省略します。

① 統合リクエストのマッピングテンプレート編集
この手順はリクエストBODYを統合レスポンスで使用したいときだけ使用します。

👉 POSTメソッド(リクエストBODY使用)の場合
デフォルトでは、リクエストBODYは統合レスポンスに渡されません。そのため、統合リクエストで明示的にオーバーライドしてあげる必要があります。
なお、statusCodeは必ず統合レスポンスに送る必要があるため、削除しないようご注意ください。
#set($context.requestOverride.path.body = $input.body) { "statusCode": 200 }


② 統合レスポンスのマッピングテンプレート編集
👉 POSTメソッド(リクエストBODY使用)の場合
リクエストBODYにidというパラメータを指定していたと仮定して記述しています。
例:{ "id": "1001" }
#set($body = $context.requestOverride.path.body) #set($id = $util.parseJson($body).id) #if($id == "1001") { "id": "1001", "title": "オフィス狛ものがたり", "Author": "yuckieee" } #elseif($id == "1002") { "id": "1002", "title": "狛ちゃん冒険譚", "Author": "yuckieee" } #else { "message":"該当の書籍情報が存在しません" } #end

なお、この辺りの話はAWS公式のデベロッパーガイドに記述されていますので、こちらも参考にしていただければと思います。
AWS公式:API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス

おまけ:APIGatewayの料金

今回使用したREST APIでは、受信したAPIコールと、転送データ量に対してのみ料金が発生します。

APIコール料金:
2021/4/13時点で、月間のリクエスト数が3億3,300万件まで100万リクエストにつき4.25USD(アジアパシフィック(東京))のようです。
※なお、キャッシュを利用する場合は、更に料金がかかりますが、テスト・開発で使用する分には必要のない機能かと思います。

データ転送料金:
2021/4/13時点で、10TBまで1GBあたり0.09USDのようです。
なお、対象はAPIGatewayから、インターネット方向のデータ(レスポンスデータ)のみが対象となります。
どちらもリクエスト数、転送データ量が多い場合は、レンジによって割引されます。

ただ、ある程度使い倒したとしても、テスト利用の範囲であれば数百円程度でしょうか。
Mockやシミュレータってどうすりゃいいのか...と、小1時間頭を抱えている工数分で解決できるなら、試してみるのも良さげです!
AWS公式:Amazon API Gateway 料金

まとめ

APIGatewayの統合Mock機能で手軽にMockやシミュレータが作成できるのが分かりました!
次回は、APIGatewayをLambdaやS3と連携して一歩進んだMock作成をご紹介できればと思います!

0 件のコメント:

コメントを投稿