3
1

More than 3 years have passed since last update.

AWS LambdaのCORSでハマったからreturnを共通化した

Posted at

はじめに

前回、AWS Lambdaを使用したサーバーレス開発について、私がハマった箇所をまとめました。
インフラエンジニアがやってみた AWSでサーバーレス Webアプリケーション開発

そこでCORSについて毎回returnでハマるのでreturnを共通化しました。
文中のAWS LambdaのソースはPython 3.8で書かれています。

言いたいこと

CORSでエラーが出たらLambdaのreturnを疑え。

Why?

JavascriptのコンソールにCORSのエラーが出るとき、十中八九Lambdaで正しくreturnしていないからです。

CORSの仕組みや考え方は以下の参考ページが大変詳しいです。
なんとなく CORS がわかる...はもう終わりにする。

ある程度仕組みを理解して頂いたところで、CORSのエラーが出るパターンは以下になります。

  • Lambda側でOPTIONSメソッドに応答していない
  • Lambda側で応答していない or 応答している内容に不備がある

かぶっている部分もありますが、大枠でこの2つになると思います。
ポカレベルの内容ですが気づきにくく、結果ハマりました。

returnを共通化する

まず、丁寧にreturnしないといけないので、それなりにreturn部の処理が長くなってしまいます。
なので共通化してreturnできるように2つの関数を作成しました。

# API Gatewayのルールに則った成功の response を生成する
def create_success_response(body, **kwargs):
    origin  = '*'
    methods = 'GET'

    for k, v in kwargs.items():
        if k == 'origin'  : origin  = v
        if k == 'methods' : methods = v 

    headers = {
        'Access-Control-Allow-Headers' : 'Content-Type',
        'Access-Control-Allow-Origin'  : origin,
        'Access-Control-Allow-Methods' : methods
    }

    logger.info(
        'return values headers = {}, body = {}, origin = {}, methods = {}'
            .format(headers, body, origin, methods)
    )

    return {
        'isBase64Encoded': False,
        'statusCode'     : 200,
        'headers'        : headers,
        'body'           : json.dumps(body)
    }

# API Gatewayのルールに則った失敗の response を生成する
def create_error_response(body, **kwargs):
    origin  = '*'
    methods = 'GET'

    for k, v in kwargs.items():
        if k == 'origin'  : origin  = v
        if k == 'methods' : methods = v 

    headers = {
        'Access-Control-Allow-Headers' : 'Content-Type',
        'Access-Control-Allow-Origin'  : origin,
        'Access-Control-Allow-Methods' : methods
    }

    return {
        'isBase64Encoded': False,
        'statusCode': 599,
        'headers': headers,
        'body': json.dumps(body)
    }

create_error_responseのほうはstatusCode599でハードコーディングしています。
要件がある場合は、methodsと同じように引数で外部から変更できるようにしてください。
originの内容もサイトのセキュリティポリシーに合わせて、URLを絞ってください。

OPTIONSメソッドに応答していない

LambdaがOPTIONSに対してちゃんと応答しているか疑いましょう。
GET, POST, PUT, DELETE…などなどは応答していたけどOPTIONSを忘れていた、という事があると思います。
というか私がそうでした。

はい、OPTIONSについての処理を追加しましょう。

OPTIONSメソッドをちゃんと処理しよう
#=======================================================
# OPTIONS : CORSに必要
#=======================================================
elif event['httpMethod'] == 'OPTIONS':
    logger.info('handle the options method.')

    return create_success_response(
        { 'message': 'successfully: called options method.' },
        methods='GET,PATCH,DELETE'
    )

この時大事なのが、methodsにLambdaで受け付けるHTTP Methodをすべて書くことです。
PUT, PATCH, POSTなど細かな違いもちゃんと仕様に合わせて書きましょう。

応答していない or 応答している内容に不備がある

returnを書いてるから応答してる!と思ったら大間違いです!
return時にちゃんと、CORSの仕様に則った応答を返す必要があります。
具体的に言うとcreate_success_responsecreate_error_response関数のheadersの部分です。
returnを共通化したので、return時に共通化した関数を呼んであげれば大丈夫になりました。

return時に共通化した関数を呼ぶ
return create_success_response(
    response,
    methods='GET'
)

どこの処理でもreturn時に関数を呼ぶようにしています。
共通化した関数を経由することで、期待されているレスポンスに適切にフォーマットした後にreturnすることが出来るようにしました。

最後に

適切なフォーマットでreturn出来ていないことが毎回のハマりポイントでした。
returnの処理を共通化することでミスに気づきやすくなりました。

javascript consoleに出力されるCORS errorにさよなら!

3
1
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
3
1