AWS S3托管网页调用API遇CORS错误及请求转换失败问题排查
问题概述
我在AWS上部署了一套轻量应用,包含一个支持PUT和OPTIONS方法、用于向S3上传文件的API。该API通过POSTMAN、Python requests及CLI的curl调用都能正常工作,但通过S3托管的静态网页调用时,触发了CORS错误。
浏览器控制台错误
Access to XMLHttpRequest at 'https://{endpoint}/v1/test.pdf' from origin 'http://{s3-bucket}.s3-website-us-east-1.amazonaws.com' 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.
CloudWatch错误日志
Execution failed due to configuration error: Unable to transform request
注:CloudWatch中可见API密钥验证已通过,但在请求体转换环节出错。
当前配置情况
S3托管桶的CORS配置
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]
API Gateway配置
- OPTIONS方法:配置了3个方法响应头:
Access-Control-Allow-Headers、Access-Control-Allow-Methods、Access-Control-Allow-Origin - 200响应的集成响应头映射:
method.response.header.Access-Control-Allow-Headers = 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token' method.response.header.Access-Control-Allow-Methods = 'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT' method.response.header.Access-Control-Allow-Origin = '*' - PUT方法采用了与OPTIONS完全相同的响应头配置。
非跨域调用验证(正常工作示例)
以下curl命令可成功执行:
curl -X PUT "https://{endpoint}.amazonaws.com/v1/test.pdf" -H "Content-Type: application/pdf" -H "x-api-key: {api-key}" --data-binary "@{file-location}\test.pdf" -v
前端JS代码(S3托管网页)
document.getElementById('uploadForm').addEventListener('submit', function(event) { event.preventDefault(); const apiKey = document.getElementById('apiKey').value; const fileInput = document.getElementById('file'); const file = fileInput.files[0]; if (!file) { alert('Please select a file.'); return; } const fileName = encodeURIComponent(file.name); const endpoint = `https://{endpoint}.execute-api.us-east-1.amazonaws.com/v1/${fileName}`; const contentType = file.type; // Perform a preflight request const preflightXhr = new XMLHttpRequest(); preflightXhr.open('OPTIONS', endpoint, true); preflightXhr.onload = function() { if (preflightXhr.status === 200) { // Proceed with the PUT request after successful preflight const xhr = new XMLHttpRequest(); xhr.open('PUT', endpoint, true); xhr.setRequestHeader('Content-Type', contentType); xhr.setRequestHeader('x-api-key', apiKey); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { alert('File uploaded successfully!'); } else { alert('Error uploading file: ' + xhr.responseText); } } }; xhr.onerror = function() { alert('An error occurred while sending the request.'); }; xhr.send(file); } else { alert('CORS preflight request failed: ' + preflightXhr.responseText); } }; preflightXhr.onerror = function() { alert('An error occurred during the CORS preflight request.'); }; preflightXhr.send(); });
排查与解决建议
检查API Gateway的OPTIONS方法集成配置
- 确认OPTIONS方法是否设置为
Mock集成(不需要实际后端),如果配置了真实后端,可能会导致请求转换错误。预请求通常不需要转发到后端,用Mock即可返回CORS头。 - 验证集成响应头的映射是否正确应用,特别是
Access-Control-Allow-Origin是否确实返回*或者对应的S3网站域名。
- 确认OPTIONS方法是否设置为
排查请求体转换错误的根源
- 检查PUT方法的请求体转换模板(Mapping Template)是否存在问题:如果API Gateway配置了针对
multipart/form-data或application/octet-stream的转换模板,可能与前端上传的文件格式不匹配,导致转换失败。 - 确认PUT方法的
Content-Type设置是否允许application/pdf等文件类型,或者直接设置为*/*来兼容所有类型。
- 检查PUT方法的请求体转换模板(Mapping Template)是否存在问题:如果API Gateway配置了针对
验证CORS头的实际返回情况
- 用curl模拟OPTIONS预请求,查看返回的响应头:
检查响应中是否包含curl -X OPTIONS "https://{endpoint}.execute-api.us-east-1.amazonaws.com/v1/test.pdf" -H "Origin: http://{s3-bucket}.s3-website-us-east-1.amazonaws.com" -H "Access-Control-Request-Method: PUT" -vAccess-Control-Allow-Origin、Access-Control-Allow-Methods等必要头信息。
- 用curl模拟OPTIONS预请求,查看返回的响应头:
检查API Gateway部署状态
- 修改CORS配置后,确保已经重新部署API到对应的阶段(Stage),未部署的配置不会生效。
前端代码优化
- 前端无需手动发送OPTIONS预请求,浏览器会自动触发。移除手动的OPTIONS请求代码,直接发送PUT请求即可,避免手动预请求可能带来的头信息缺失问题。
内容的提问来源于stack exchange,提问作者Stephen Strosko
相关产品推荐
相关产品推荐

