[AWS CDK] API Gateway REST API の「ゲートウェイレスポンス」を設定して、API レベルのエラー発生時に CORS エラーを発生させないようにする

2023.10.27

こんにちは、CX 事業本部 Delivery 部の若槻です。

Amazon API Gateway REST API では ゲートウェイレスポンス を設定することにより、API レベルのエラー発生時に任意のレスポンスを返すことができます。

今回は、AWS CDK で REST API の「ゲートウェイレスポンス」を設定して、API レベルのエラー発生時に CORS エラーを発生させないようにしてみました。

既定では API レベルのエラー発生時は CORS エラーが発生する

REST API のゲートウェイの設定メニューは次のようになっています。既定ではどのエラーでもレスポンスヘッダーが設定されていません。

よって、例えば Authorizer が設定された API に対して、認証されていない状態で Web 画面からリクエストを送信すると、レスポンスに CORS 許可用のヘッダーは設定されないため、次のようにブラウザ側で CORS エラーが発生します。

CORS エラーが発生することにより、クライアント側でのエラーハンドリングやデバッグが難しくなります。

ゲートウェイレスポンスを設定する

API レベルのエラー発生時に任意のレスポンスを返すには、ゲートウェイレスポンスを設定します。ここではすべてのエラーに対して、CORS 許可用ヘッダーを設定するようにしてみます。

lib/constructs/api.ts

import {
  aws_lambda,
  aws_lambda_nodejs,
  aws_apigateway,
  aws_cognito,
  Duration,
} from "aws-cdk-lib";
import { Construct } from "constructs";

interface ApiConstructProps {
  cognitoUserPool: aws_cognito.UserPool;
}

export class ApiConstruct extends Construct {
  constructor(scope: Construct, id: string, props: ApiConstructProps) {
    super(scope, id);

    const { cognitoUserPool } = props;

    const restApiFunc = new aws_lambda_nodejs.NodejsFunction(
      this,
      "RestApiFunc",
      {
        architecture: aws_lambda.Architecture.ARM_64,
        entry: "../server/src/lambda/handlers/api-gateway/rest-api-router.ts",
        tracing: aws_lambda.Tracing.ACTIVE,
      },
    );

    const cognitoUserPoolsAuthorizer =
      new aws_apigateway.CognitoUserPoolsAuthorizer(
        this,
        "CognitoUserPoolsAuthorizer",
        {
          cognitoUserPools: [cognitoUserPool],
        },
      );

    const restApi = new aws_apigateway.LambdaRestApi(this, "RestApi", {
      handler: restApiFunc,
      defaultCorsPreflightOptions: {
        allowOrigins: aws_apigateway.Cors.ALL_ORIGINS,
        allowMethods: aws_apigateway.Cors.ALL_METHODS,
        allowHeaders: aws_apigateway.Cors.DEFAULT_HEADERS,
        maxAge: Duration.minutes(5),
      },
      deployOptions: {
        stageName: "v1",
        tracingEnabled: true,
      },
      defaultMethodOptions: { authorizer: cognitoUserPoolsAuthorizer },
    });

    restApi.addGatewayResponse("Default4xx", {
      type: aws_apigateway.ResponseType.DEFAULT_4XX,
      responseHeaders: {
        "Access-Control-Allow-Origin": "'*'",
        "Access-Control-Allow-Headers": "'*'",
        "Access-Control-Allow-Methods": "'*'",
      },
    });

    restApi.addGatewayResponse("Default5xx", {
      type: aws_apigateway.ResponseType.DEFAULT_5XX,
      responseHeaders: {
        "Access-Control-Allow-Origin": "'*'",
        "Access-Control-Allow-Headers": "'*'",
        "Access-Control-Allow-Methods": "'*'",
      },
    });
  }
}

API Construct に対して addGatewayResponse でゲートウェイレスポンスを設定しています。DEFAULT_4XX および DEFAULT_5XX に対して設定することにより、すべてのエラーに対して CORS 許可用ヘッダーを設定することができます。必要に応じてヘッダー値を絞ってください。

CDK デプロイすると、すべてのエラーに対してレスポンスヘッダーが設定されます。

改めて認証されていない状態で Web 画面からリクエストを送信すると、今度は CORS エラーは発生せず 401 (Inauthorized) エラーのみ表示されるようになりました。

おまけ

前述の動作確認でも使用した CORS エラーを簡易的に確認する Web 画面のソースコードです。ただし任意のリクエストヘッダーを指定可能とするなど改良の余地はあります。

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>CORS Checker</title>
  </head>
  <body>
    <h1>CORS Checker</h1>
    <form>
      <label for="endpoint">API Gateway Endpoint:</label>
      <input type="text" id="endpoint" name="endpoint" />
      <button type="submit">Check CORS</button>
    </form>
    <div id="result"></div>
    <script>
      const form = document.querySelector("form");
      const resultDiv = document.querySelector("#result");

      form.addEventListener("submit", async (event) => {
        event.preventDefault();
        const endpoint = document.querySelector("#endpoint").value;
        try {
          const response = await fetch(endpoint);
          const corsHeaders = response.headers.get("access-control-allow-origin");
          resultDiv.textContent = `CORS headers: ${corsHeaders}`;
        } catch (error) {
          resultDiv.textContent = "Error occurred while fetching CORS headers";
        }
      });
    </script>
  </body>
</html>

おわりに

AWS CDK で REST API の「ゲートウェイレスポンス」を設定して、API レベルのエラー発生時に CORS エラーを発生させないようにしてみました。

できれば目にはしたくない CORS エラー、簡単に抑制することができるのでぜひお試しください。

以上