こんにちは、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 エラー、簡単に抑制することができるのでぜひお試しください。
以上