使用 BOTO3 为 AWS Api Gateway 自动化 CORS

Posted

技术标签:

【中文标题】使用 BOTO3 为 AWS Api Gateway 自动化 CORS【英文标题】:Automating CORS using BOTO3 for AWS Api Gateway 【发布时间】:2016-10-29 09:47:48 【问题描述】:

过去几个小时我一直在尝试为 AWS Api 网关配置 CORS。我试图逐字复制 aws 控制台中“启用 CORS”按钮的作用。但是即使每个方法在控制台中看起来都一样,POST 到其余 API 也可以使用“启用 CORS”按钮,但在使用我的代码设置 CORS 时会返回 500 权限错误。

这是与 CORS 设置相关的代码:

# Set the put method response of the POST method
self.apigateway.put_method_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='POST',
    statusCode='200',
    responseParameters=
        'method.response.header.Access-Control-Allow-Origin': False
    ,
    responseModels=
        'application/json': 'Empty'
    
)

# Set the put integration response of the POST method
self.apigateway.put_integration_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='POST',
    statusCode='200',
    responseParameters=
        'method.response.header.Access-Control-Allow-Origin': '\'*\''
    ,
    responseTemplates=
        'application/json': ''
    
)

# Add an options method to the rest api
api_method = self.apigateway.put_method(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    authorizationType='NONE'
)

# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    type='MOCK',
    requestTemplates=
        'application/json': ''
    
)

# Set the put method response of the OPTIONS method
self.apigateway.put_method_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    statusCode='200',
    responseParameters=
        'method.response.header.Access-Control-Allow-Headers': False,
        'method.response.header.Access-Control-Allow-Origin': False,
        'method.response.header.Access-Control-Allow-Methods': False
    ,
    responseModels=
        'application/json': 'Empty'
    
)

# Set the put integration response of the OPTIONS method
self.apigateway.put_integration_response(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    statusCode='200',
    responseParameters=
        'method.response.header.Access-Control-Allow-Headers': '\'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token\'',
        'method.response.header.Access-Control-Allow-Methods': '\'POST,OPTIONS\'',
        'method.response.header.Access-Control-Allow-Origin': '\'*\''
    ,
    responseTemplates=
        'application/json': ''
    
)

这是通过 AWS 控制台启用 CORS 时 POST 和 OPTIONS 的 get-method 的响应:


    "httpMethod": "POST",
    "apiKeyRequired": false,
    "methodIntegration": 
        "httpMethod": "POST",
        "cacheKeyParameters": [],
        "integrationResponses": 
            "200": 
                "responseParameters": 
                    "method.response.header.Access-Control-Allow-Origin": "'*'"
                ,
                "statusCode": "200",
                "responseTemplates": 
                    "application/json": null
                
            
        ,
        "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
        "requestTemplates": 
            "application/json": null
        ,
        "cacheNamespace": "o9h9b8tzo2",
        "type": "AWS"
    ,
    "methodResponses": 
        "200": 
            "responseParameters": 
                "method.response.header.Access-Control-Allow-Origin": false
            ,
            "statusCode": "200",
            "responseModels": 
                "application/json": "Empty"
            
        
    ,
    "authorizationType": "NONE"


    "requestParameters": ,
    "httpMethod": "OPTIONS",
    "methodResponses": 
        "200": 
            "statusCode": "200",
            "responseParameters": 
                "method.response.header.Access-Control-Allow-Headers": false,
                "method.response.header.Access-Control-Allow-Methods": false,
                "method.response.header.Access-Control-Allow-Origin": false
            ,
            "responseModels": 
                "application/json": "Empty"
            
        
    ,
    "apiKeyRequired": false,
    "methodIntegration": 
        "cacheNamespace": "o9h9b8tzo2",
        "type": "MOCK",
        "requestTemplates": 
            "application/json": "\"statusCode\": 200"
        ,
        "integrationResponses": 
            "200": 
                "responseTemplates": 
                    "application/json": null
                ,
                "statusCode": "200",
                "responseParameters": 
                    "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
                    "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
                    "method.response.header.Access-Control-Allow-Origin": "'*'"
                
            
        ,
        "cacheKeyParameters": []
    ,
    "authorizationType": "NONE"

这是使用我的代码启用 CORS 的 get-method 的响应:


    "authorizationType": "NONE",
    "httpMethod": "POST",
    "methodIntegration": 
        "requestTemplates": 
            "application/json": null
        ,
        "cacheNamespace": "308o168qal",
        "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:477869670267:function:controller/invocations",
        "httpMethod": "POST",
        "cacheKeyParameters": [],
        "integrationResponses": 
            "200": 
                "responseParameters": 
                    "method.response.header.Access-Control-Allow-Origin": "'*'"
                ,
                "statusCode": "200",
                "responseTemplates": 
                    "application/json": null
                
            
        ,
        "type": "AWS"
    ,
    "apiKeyRequired": false,
    "methodResponses": 
        "200": 
            "responseParameters": 
                "method.response.header.Access-Control-Allow-Origin": false
            ,
            "responseModels": 
                "application/json": "Empty"
            ,
            "statusCode": "200"
        
    


    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "methodIntegration": 
        "integrationResponses": 
            "200": 
                "statusCode": "200",
                "responseTemplates": 
                    "application/json": null
                ,
                "responseParameters": 
                    "method.response.header.Access-Control-Allow-Methods": "'POST,OPTIONS'",
                    "method.response.header.Access-Control-Allow-Origin": "'*'",
                    "method.response.header.Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
                
            
        ,
        "cacheNamespace": "bm4zmvzkdk",
        "type": "MOCK",
        "cacheKeyParameters": [],
        "requestTemplates": 
            "application/json": "\"statusCode\": 200"
        
    ,
    "requestParameters": ,
    "methodResponses": 
        "200": 
            "statusCode": "200",
            "responseModels": 
                "application/json": "Empty"
            ,
            "responseParameters": 
                "method.response.header.Access-Control-Allow-Methods": false,
                "method.response.header.Access-Control-Allow-Origin": false,
                "method.response.header.Access-Control-Allow-Headers": false
            
        
    ,
    "httpMethod": "OPTIONS"

我看不到任何区别,我做错了什么?

根据 MikeD 在 AWS 的请求,POST 请求是从 s3 中的文件中的 javascript 发出的:

function post_request() 
    var xhr = new XMLHttpRequest();
    var params = JSON.stringify(
        request: "registerUser",
        user: 
            username: document.getElementById("usernameInput").value, 
            email: document.getElementById("emailInput").value,
            password: document.getElementById("passwordInput").value
        
    );
    xhr.open("POST", "$(endpoint_url)", true);
    xhr.setRequestHeader("Content-type", "application/json");
    xhr.setRequestHeader("x-api-key", "$(api_key)");
    xhr.onreadystatechange = function()
        if(xhr.readyState === 4)
            if(xhr.status === 200)
                alert("You are registered!");
            
            else
                alert("Could not register. Please try again later.");
            
        
    ;
    xhr.send(params);
    return false;

我的设置脚本将 $(endpoint_url) 和 $(api_key) 替换为适当的值(我已确认这些值是准确的)。

这是发出 POST 请求时来自 chrome 控制台的逐字响应:

register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39 OPTIONS https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod post_request @ register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:39document.getElementById.onsubmit @ register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:44
register.html?X-Amz-Date=20160628T070211Z&X-Amz-Expires=300&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-…:1 XMLHttpRequest cannot load https://dn9sjxz0i9.execute-api.us-east-1.amazonaws.com/prod. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://s3.amazonaws.com' is therefore not allowed access. The response had HTTP status code 500.

【问题讨论】:

"mine doesn't work" 并没有真正让我们继续下去。在什么意义上它不起作用?我在这里看到的只是配置,而不是观察到的行为示例或预期行为的描述。您是否使用curl 之类的工具捕获了请求和响应标头?结果如何? 对不起,我在这方面还很陌生。我添加了对错误(500 错误)和 API Gateway 获取方法的输出的说明。感谢您的反馈:) 你是如何调用 API 的?如果通过 curl,您能否发布 OPTIONS 调用和 POST 调用的详细请求/响应详细信息。如果通过浏览器,您可以发布浏览器所做的所有事情的日志吗? (大多数浏览器都有工具可以让您查看浏览器发出的 CORS 请求以及它得到的响应。) 我已经添加了 chrome 控制台输出以及用于发送 post 请求的代码。感谢您查看我的问题。 我还没有找到任何东西。您是否记得在进行 CORS 相关更改后重新部署您的 API? 【参考方案1】:

OPTIONS 方法的 put 集成需要有一个映射模板,其中包含一个值为 200 的 statusCode。看起来您的代码将映射模板设置为空字符串 ('')。当您通过 API Gateway 创建集成时,它会添加一个默认映射模板:"statusCode": 200

将相同的映射模板添加到您的 put 集成中,如下所示:

# Set the put integration of the OPTIONS method
self.apigateway.put_integration(
    restApiId=self.rest_api['id'],
    resourceId=root_resource['id'],
    httpMethod='OPTIONS',
    type='MOCK',
    requestTemplates=
        'application/json': '"statusCode": 200'
    
)

【讨论】:

以上是关于使用 BOTO3 为 AWS Api Gateway 自动化 CORS的主要内容,如果未能解决你的问题,请参考以下文章

API 无法识别向 AWS Boto3 Cognito-idp 传递参数

AWS:如何使用 Boto3 从实例创建公共 AMI?

Boto3 AWS KMS 加密解密文件

boto3或aws cli是否具有从实例创建模板?

使用boto3 Python SDK返回AWS EC2可用性区域属性

AWS RDS极光 - 通过boto3或aws cli创建跨区域读取副本