LoginSignup
10
2

More than 1 year has passed since last update.

AWS SAMのCORSを設定する(REST APIバージョン)

Last updated at Posted at 2021-12-10

困っていたこと

ローカル開発環境で、ブラウザでReactからAWS SAMのAPI Gatewayを叩きたかったのですが、CORSに行く手を阻まれておりました。

下記は、CORS未設定の状態でAPIを叩いたときにブラウザのコンソールに出るエラーメッセージです。

Access to fetch at ‘http://127.0.0.1:3001/hello_world’ from origin ‘http://127.0.0.1:3000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

噛み砕くと、
‘http://127.0.0.1:3001/hello_world’へのアクセスはCORSポリシーによってブロックされて、preflightリクエストはアクセスコントロールチェックをパスできなかったよ。だって、リクエストされているリソースのヘッダーにCORSを許可する記述がないんだもん。かくかくしかじか...
的なことが書いてあるのだと思います。

ちなみにCORSとは、Cross-Origin Resource Sharingの略で、オリジンAのリソースを表示しているブラウザからリクエストを受けた別のオリジンBがそのリクエストを許可してレスポンスを返す設定、という理解で合ってると思います。。怪しいと思う方はこちらを。

実装!

以下2つの設定が必要です。

  1. API GatewayにCORSを設定する(SAMのtemplate.yamlにて)
  2. LambdaのレスポンスヘッダーにCORS許可の設定を書いてあげる

1. API GatewayにCORSを設定する(SAMのtemplate.yamlにて)

ドキュメントはこの辺を参照しました。→(AWS::Serverless::Api#cors

template.yamlに記述していきますが、Globalsに書く方法とResourcesに書く方法の2パターンあります。何が違うかというと、Globalsに書けば一度の宣言でプロジェクト内のリソースに継承されるそうです。(ドキュメント:AWS SAM テンプレートの Globals セクション

1-1. Globalsに書く

template.yaml
Globals:
  Function:
    Timeout: 30
  Api:
    Cors:
      AllowOrigin: "'*'"
      AllowCredentials: true
      AllowMethods: "'POST'"
      AllowHeaders: "'Content-Type,X-CSRF-TOKEN'"

"'*'"というシングルクォテーションとダブルクォテーションを両方使う書き方がマストみたいです。どちらかだけのクォテーションにしてしまうとError: AllowHeaders must be a quoted string (i.e. "'value'" is correct, but "value" is not).てな具合に怒られます。

まだこれだけではCORSはきちんと設定できていませんが、試しに一旦動作確認してみると、こんなエラー文になります。

Access to fetch at 'http://127.0.0.1:3003/student_event_logs' from origin 'http://127.0.0.1:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

さっきと同じエラーに見えますが、Response to preflight request doesn’t pass access control checkというpreflightに関するエラー文が消えました。

開発者ツールでNetworkを確認してみると、確かにpreflightは通っています。(MDN: preflightとは

スクリーンショット 2021-11-12 11.36.40.png

1-2. GlobalsではなくResourcesApiに書く(REST API)

ここでもやはり、"'*'"というシングルクォテーションとダブルクォテーションが必要な文法になっています。
以下が例です。

template.yaml
Globals:
  Function:
    Runtime: ruby2.7
    Timeout: 3

Resources:
  HelloWorldLogsApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: 'dev'
      Cors:
        AllowOrigin: "'http://127.0.0.1:3000'"
        AllowCredentials: true
        AllowMethods: "'POST'"
        AllowHeaders: "'Content-Type,X-CSRF-TOKEN'"

  HelloWorldLogsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello_world_logs/
      Handler: app.lambda_handler
      Runtime: ruby2.7
      Events:
        ApiEvent:
          Type: Api
          Properties:
            RestApiId: !Ref HelloWorldLogsApi
            Path: /hello_world_logs
            Method: post

2. LambdaのレスポンスヘッダーにCORS許可の設定を書いてあげる

app.rb(Lambda)
require 'json'

def lambda_handler(event:, context:)
  {
    statusCode: 200,
    body: {
      message: "hello world",
    }.to_json,
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Credentials": true,
      "Access-Control-Allow-Methods": "POST",
      "Access-Control-Allow-Headers": "Content-Type,X-CSRF-TOKEN",
    }
  }
end

template.yamlと違い、"'*'"とすると、エラーになります。

3. 動作確認

この通り、先ほどのOPTIONS(preflight)に続き、POSTリクエストも成功しました。
スクリーンショット 2021-11-12 11.36.57.png
ちなみにローカルで確認するだけなら、ここまでの間、sam buildsam deployはしなくても大丈夫です。

最後に

この記事はREST APIでのCORS設定ですが、HTTP APIでのやり方は別記事としてまとめました→「AWS SAMでHTTP APIを実装する。CORSも設定する。REST APIと微妙に違う!

10
2
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
10
2