APIGW リクエスト検証とは?実装する方法までわかりやすく解説!-その1

記事タイトルとURLをコピーする

はじめに

こんにちは。孔子の80代目子孫兼ディベロップメントサービス課の孔です。3月から配属が変わり、ディベロップメントサービス課配属となりましたが、どうも自分の部署名が覚えられなくて苦労しているところです。DSと略されるので、どうしても任天堂しか連想されないのが苦労ポイントですね。

今日お話する内容はリクエスト検証(Request Validation)に関する話となります。APIGWはサーバーレスアーキテクチャーを構築する上で欠かせないサービスの一つですね。このAPIGWですが、基本的な機能であるAPIの作成・公開以外にも、実は多くの機能を持っています。今日は数ある機能の中の一つである、リクエスト検証に関する話となります。それでは、早速みてみましょう!

※ 本ブログはAPIGWの基礎知識の解説はありません。APIGWに関する簡単な説明は以下のリンクをご参照ください。

aws.amazon.com

リクエスト検証とは?

AWSの公式ドキュメントはこちらのリンクになります。豊富な関連ドキュメントもありますので、このブログを読んで追加でもっといろいろな内容が知りたい方は後ほどこちらのリンクからいろいろ辿ってみるのもいいと思います。

それでは、まずはリクエスト検証の概要から見てみましょう!

概要

リクエスト検証を簡単に説明すると、APIGWに投げられたリクエストが正常かどうかをAPIGWで評価し、その結果に応じて事前に設定されたレスポンスを返すAPIGWの機能となります。

ここで「正常かどうかをAPIGWで評価」とはどういったことでしょう?例えば、Web掲示板に書き込みをする時を考えてみましょう。書き込みをするときに、ユーザーはウェブサーバに「この記事を投稿したい!」とリクエストをします。すると、ウェブサーバはどのユーザーが送ったリクエスト文に一緒に送られてきたユーザー名、タイトル、本文でどのような掲示物かを判断し、記事を作成します。ですので、書き込みのリクエストをする際に正しいリクエスト文には絶対「ユーザー名」と「タイトル」、「本文」を含んでいるはずです。

f:id:swx-kong:20210313070421p:plain

しかし、どれか一つでも要素が足りなかったらどうなるんでしょうか?記載されるはずの要素が記載されてないので、サーバはそのリクエストを受け付けてもどうしようもないですね。この時のリクエストは正常と言えないわけです。

f:id:swx-kong:20210313070432p:plain

つまり、リクエスト検証機能は、このようにリクエストがヘッダーもしくはボディーに適切な値を含めているかどうかをAPIGW側でチェック(評価)し、そのチェックした結果に合わせて適切な動作をする機能となります。

何が嬉しいの?

リクエスト検証のメリットと言えば、「コードに余計な処理をさせなくて済む」ところになります。先ほどのサーバーは、タイトルがないことがわかったら、「タイトルを入力してください」というレスポンスを返すとします。本来であればユーザー名とタイトル、本文を受け取って記事にすれば任務完了だったはずのサーバーに、余計の処理が必要になりますね。

以下のようなLambdaコードをみてください(コードはわかりやすく説明するため、実際動くコードを少し変形しています)

def lambda_handler(event, context):
    author = event['body']['author']
    title = event['body']['title']
    content = event['body']['content']
    
    make_article(author, title, content)

このコードには致命的な問題点があります。それは「そもそもリクエスト文のボディにユタイトルなどが含まれてなかったらどうする?」が考慮されてないんですね。なのでもしリクエストの際にユーザーが間違えてタイトルを記載せずに送ったら、読みにくいエラー文を返すことになります。なので、コードを以下のように修正する必要があります。

def lambda_handler(event, context):
    try:
        author = event['body']['author']
        title = event['body']['title']
        content = event['body']['content']

        make_article(author, title, content)

    except KeyError:
        return "Wrong request"

これで対応完了です。しかし、まだ問題があります。それは必要なパラメータが増えたり、ヘッダーもチェックしたりすると評価だけで長いコードを書くことになり、コードも汚れてしまうので設計をやり直したりする必要が生じてきます。だったら「リクエストボディにユーザー名やタイトルが含まれること前提で話を進めることができたならいいのにな〜」って思っちゃいますね。それを実現してくれるのがこのリクエスト検証機能になるのです。DbC(Design by Contract、契約による設計)で言われる「事前条件」に値する役割です。

正常でないリクエストを送られたことによって起き得るコードの副作用・バグの可能性も事前に遮断できるのも大きなメリットになりますので、積極的に検討した方がいいですね。

それでは、リクエスト検証がどのようなものなのかおよびメリットもご理解いただけたかと思いますので、実際作ってみましょう。まずはAWSコンソールを操作して作ってみます。

APIGWコンソール上でリクエスト検証を実装する

事前準備

まずはAPIGWを作りましょう。今回はREST APIのサンプルAPIGWを使用します。作成方法はこちらのリンクに記載されていますので、ご参照ください。

ヘッダーのリクエスト検証設定

作成が完了しましたら、「POSTメソッド」→「メソッドリクエスト」順にクリックしてください。

f:id:swx-kong:20210309022532p:plain

すると、リクエストメソッドの設定が行える以下の画面に遷移します。ここでリクエスト検証機能をオンにできます。

f:id:swx-kong:20210309023113p:plain

まずは鉛筆マークをクリックし、プルダウンから「クエリ文字列パラメータおよびヘッダーの検証」を選択して右側のチェックボタンをクリックします。

f:id:swx-kong:20210309023852p:plain

その後、「HTTP リクエストヘッダー」→「ヘッダーの追加」順にクリックし、名前に「testHeader」を入れて保存します。新しく作成された「testHeader」項目は必須チェックをつけてください。正しく操作ができたら以下のようになります。

f:id:swx-kong:20210309023926p:plain

これでAPIGWは今必須と設定したヘッダーがついているかどうかをチェックしてくれるようになります。それでは動作確認です。左上の「メソッドの実行」ボタンをクリックし、最初の画面に戻ります。遷移した画面で「テスト」をクリックしてください。するとAPIをテストできる画面になりますので、何も入力せずに下段にある「テスト」ボタンをクリックしてみましょう。400のステータスコードと以下レスポンスが本文に記載されていたらOKです。「testHeader」ヘッダーを必須にするよう設定したので、それをつけずにリクエストを送ったら「間違えたリクエストだよ!」とAPIGW側で判断し、適切なステータスコードとエラーメッセージを返してくれてます。

f:id:swx-kong:20210309024311p:plain

「testHeader」ヘッダーをつけてリクエストを送信すると200ステータスコードが返却されることが確認できます。

f:id:swx-kong:20210309030719p:plain

パラメータの設定はヘッダーと同様のやり方で設定ができるので、省略します。

ボディのリクエスト検証設定

先ほどの「メソッドリクエスト」画面に戻りましょう。テストのページの左上の「メソッドの実行」をクリックし、「メソッドリクエスト」をクリックしてください。まず先ほど設定した「リクエストの検証」設定を「本文の検証」に変更してください。これで本文(ボディ)もちゃんとAPIGW側でチャックしてくれるようになります。

下をみると「リクエスト本文」という設定欄があります。開いてみるともう設定がされていることがわかります。

f:id:swx-kong:20210309031235p:plain

ここで「モデル」という概念が登場します。「モデル」とは、リクエストボディがどのような構造で送られるはずなのか、を定義したものとなります。具体例をみてみましょう。左のナビゲーションをみると、「モデル」項目があります。クリックしてみましょう。

f:id:swx-kong:20210309025213p:plain

先ほどみた設定では「NewPet」が指定されていたので、いくつかあるモデルの中で「NewPet」をクリックしてください。以下のような設定がされていることが確認できます。

{
  "type" : "object",
  "properties" : {
    "type" : {
      "$ref":"https://apigateway.amazonaws.com/restapis/XXXXXXXXX/models/PetType"
    },
    "price" : {
      "type" : "number"
    }
  }
}

ここで「properties」に注目してください。「type」と「price」が指定されています。つまり、この記述が話していることは「リクエストボディはtypepriceを含めないといけません。また、typePetTypeモデルに従い、pricenumber型でないといけません」とのことになります。ついでにPetTypeも確認してみると、以下のようになってます。

{
  "type" : "string",
  "enum" : [ "dog", "cat", "fish", "bird", "gecko" ]
}

これは「文字列であり、含むことができる文字列は "dog", "cat", "fish", "bird", "gecko" の中のどれか」になります。それでは、テストです。

左側のナビゲーションから「リソース」をクリックし、「POST」→「テスト」をクリックしてテスト画面に遷移します。何も設定せずに下の「テスト」ボタンを押すと、400ステータスコードとともに"Invalid request body"が帰ってきます。

f:id:swx-kong:20210309030910p:plain

モデルにしたがってボディを記入してみましょう。まず「type」と「price」が必要とのことだったので、記載します。また、「type」は文字列で "dog", "cat", "fish", "bird", "gecko"のどれか、「price」は数字なので任意の数字を入れます。以下例ですので、よろしければリクエスト本文にそのままコピペしてご使用ください。

{
    "type": "dog",
    "price": 1200
}

テストをクリックすると以下のように200ステータスコードとともにレスポンスが返却されます。

f:id:swx-kong:20210309031819p:plain

最後に

リクエスト検証機能の概念、メリット、そしてコンソール上での実装方法までみてみましたが、いかがでしたか?上手く使いこなせばLambda関数の負担軽減、レスポンス速度の向上、レスポンス管理が用意になるなど、本文では紹介なかったメリットもたくさん享受できるとてもいい機能ですので、ぜひ使い方をいろいろ考えてみてください。

次回のブログは番外編となりますが、APIGW自体がサーバーレス構成でよく使用されるリソースなので、Serverless Frameworkを使ってどのように構築できるのかを紹介したいと思います。それでは、また次のブログで会いましょう!

次のブログのURLはこちら:https://blog.serverworks.co.jp/request-validation-with-apigw-2