我正在尝试在我的 AWS SAM 应用程序中启用 CORS。这是我的
template.yaml
的片段:
Globals:
Api:
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
Resources:
MyApi:
Type: AWS::Serverless::Api
Properties:
StageName: prod
Auth:
Authorizers:
MyCognitoAuthorizer: ...
getByIdFunc:
Type: AWS::Serverless::Function
Properties:
Handler: src/handler.handle
Events:
ApiEvent:
Type: Api
Properties:
Path: /{id}
Method: GET
RestApiId: !Ref MyApi
根据此Using CORS with AWS SAM和https://github.com/aws/serverless-application-model/issues/373,cors配置应该可以工作,但遗憾的是API响应上没有设置标头,如下所示。
< HTTP/2 200
< content-type: application/json
< content-length: 770
< date: Tue, 13 Apr 2021 19:55:31 GMT
< x-amzn-requestid: ...
< x-amz-apigw-id: ...
< x-amzn-trace-id: Root=1-...-...;Sampled=0
< x-cache: Miss from cloudfront
< via: 1.1 ...cloudfront.net (CloudFront)
< x-amz-cf-pop: FRA2-C2
< x-amz-cf-id: ...==
<
* Connection #0 to host ....execute-api.eu-central-1.amazonaws.com left intact
[{"model": ..}]
我还尝试将 cors 配置添加到 API 定义 (MyApi) 本身,如官方文档中所述,但没有成功。
我可以自己在响应中添加标头,但我宁愿将其放在模板文件中。
为我解决这个问题的方法是将以下内容添加到我的 template.yaml 中:
Globals:
Api:
Cors:
AllowMethods: "'GET,POST,OPTIONS'"
AllowHeaders: "'content-type'"
AllowOrigin: "'*'"
# AllowCredentials: true Uncomment only if you choose a specific origin instead of the * wildcard.
就像 nirvana124 和 Nitesh 所说,您还需要在每个端点中返回这些标头和响应:
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "GET" // Allow only GET request
},
body: JSON.stringify(response)
}
使用 SAM/CloudFormation 或 AWS 控制台,您可以为 OPTIONS 方法设置 CORS 标头,该方法将在调用实际 API 方法(GET/POST 等)之前由浏览器调用。邮递员或任何其他服务只会调用您的端点。
当我们将 lambda 代理与 API Gateway 一起使用时,我们需要在 lambda 的响应对象中设置 CORS 标头。
要为 Lambda 代理集成启用 CORS,您必须将 Access-Control-Allow-Origin:
添加到输出标头。域名可以是任何域名的domain-name
。*
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "GET" // Allow only GET request
},
body: JSON.stringify(response)
}
对于使用 API Gateway 版本 2 (HttpApi) 的任何人来说,它不支持“AddDefaultAuthorizerToCorsPreflight”,至少在其文档中没有指定。
相反(根据 AWS guidelines),我们需要在 OPTIONS /{proxy+} 的 lambda 函数中添加一条路由,并关闭该路由的身份验证。
MyHttpApi:
Type: AWS::Serverless::HttpApi
Properties:
Auth:
DefaultAuthorizer: OAuth2
Authorizers:
OAuth2:
JwtConfiguration:
issuer: "..."
audience:
- ...
IdentitySource: "$request.header.Authorization"
CorsConfiguration:
AllowOrigins:
- "*"
AllowMethods:
- GET
- POST
- OPTIONS
AllowHeaders:
- Content-Type
- Accept
- Access-Control-Allow-Headers
- Access-Control-Request-Method
- Access-Control-Request-Headers
- Authorization
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Events:
CorsPreflightEvent:
Type: HttpApi
Properties:
Path: /{proxy+}
Method: OPTIONS
Auth:
Authorizer: NONE
ApiId: !Ref MyHttpApi
使用您的脚本,在 API Gateway 上启用了 CORS,但 SAM 始终创建从 API Gateway 到 Lambda 的 PROXY 集成,这意味着 API Gateway 无法添加任何集成响应,它将传递从 LAMBDA 收到的响应。这就是为什么您需要在 lambda 内部处理 CORS 标头,而不是依赖 API 网关。下面是从 LAMBDA 返回的示例代码以及响应。
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with",
"Access-Control-Allow-Origin": "*", // Allow from anywhere
"Access-Control-Allow-Methods": "OPTIONS,POST,GET,PUT,DELETE,PATCH" // Allow only GET request
},
body: JSON.stringify(response)
}
迟到了,但对于 Google 的其他人来说:将其添加到 Auth 部分,这将不允许授权者处理 CORS HTTP 标头
Api:
Auth:
AddDefaultAuthorizerToCorsPreflight: false
要在 API 网关和 CloudFormation/SAM 上启用 Cors,我们需要做一些事情:
这是一个示例文件,显示了如何定义它:
AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
SignupApi:
Type: 'AWS::Serverless::Api'
Properties:
StageName: prod
Cors:
AllowMethods: "'POST'"
AllowHeaders: "'Content-Type'"
AllowOrigin: "'*'"
DefinitionBody:
swagger: '2.0'
info:
title: 'Signup API'
version: '1.0.0'
paths:
/signup:
options:
summary: CORS support
description: |
Enable CORS by returning correct headers
consumes:
- application/json
produces:
- application/json
tags:
- CORS
x-amazon-apigateway-integration:
type: mock
requestTemplates:
application/json: |
{
"statusCode" : 200
}
responses:
"default":
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: |
{}
responses:
'200':
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
post:
produces:
- application/json
responses:
'200':
description: Default response for CORS method
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/SignupRequest'
x-amazon-apigateway-integration:
uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${SignupFunction.Arn}/invocations'
passthroughBehavior: 'when_no_match'
httpMethod: 'POST'
type: 'aws'
integrationHttpMethod: 'POST'
requestTemplates:
application/json: |
#set($allHeaders = $input.params().header)
{
#foreach($header in $allHeaders.keySet())
"$header": "$util.escapeJavaScript($allHeaders.get($header))" #if($foreach.hasNext),#end
#end
"body": $input.json("$.body")
}
responses:
default:
statusCode: '200'
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: '$input.json("$.body")'
definitions:
SignupRequest:
type: 'object'
properties:
name:
type: 'string'
email:
type: 'string'
password:
type: 'string'
SignupFunction:
Type: 'AWS::Serverless::Function'
Properties:
Handler: index.lambda_handler
Runtime: python3.8
InlineCode: |
import json
def lambda_handler(event, context):
response = {
'statusCode': 200,
'body': json.dumps({'message': 'Signup successful'})
}
return response
Events:
SignUp:
Type: Api
Properties:
Path: /signup
Method: post
RestApiId: !Ref SignupApi