如何解决AWS API Gateway集成Lambda的CORS跨域错误
问题描述
我有一个AWS Lambda函数,用于调用第三方接口获取所有国家名称,已关联AWS API Gateway。用Postman发送GET请求https://AWS-ID.execute-api.us-east-1.amazonaws.com/locations?location=all能正常返回结果,但在HTML页面中调用时遇到CORS错误。
Lambda函数代码
import axios from 'axios'; export const handler = async (event) => { try { console.log('EVENT'); console.log(JSON.stringify(event, null, 2)); if (event.rawPath === '/locations') { if (event.rawQueryString === 'location=all') { const countriesResponse = await axios.get('https://countriesnow.space/api/v0.1/countries/states'); const countryNames = countriesResponse.data.data.map(country => country.name); return { statusCode: 200, headers: { "Access-Control-Allow-Headers" : "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify(countryNames), }; } else { return { statusCode: 400, body: JSON.stringify({ message: 'Invalid endpoint' }) }; } } else { return { statusCode: 400, body: JSON.stringify({ message: 'Invalid endpoint' }) }; } } catch (error) { console.error('Error:', error); return { statusCode: 500, body: JSON.stringify({ message: 'Internal Server Error' }) }; } };
前端HTML代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Country Dropdowns</title> </head> <body> <label for="country">Select Country:</label> <select id="country" onchange="fetchCountries()"> <option value="">Select</option> </select> <br> <label for="state">Select State:</label> <select id="state"> <option value="">Select</option> </select> <script> // Function to fetch countries from the API function fetchCountries() { fetch("https://AWS-ID.execute-api.us-east-1.amazonaws.com/locations?location=all") .then(response => { if (!response.ok) { console.log(response) throw new Error(`HTTP error! Status: ${response.status}`); } return response.json(); }) .then(data => { console.log("Fetched countries:", data); const countryDropdown = document.getElementById("country"); data.locations.forEach(country => { const option = document.createElement("option"); option.value = country.code; option.textContent = country.name; countryDropdown.appendChild(option); }); }) .catch(error => console.error('Error fetching countries:', error)); } // Fetch countries when the page loads fetchCountries(); </script> </body> </html>
遇到的CORS错误
Access to fetch at 'https://AWS-ID.execute-api.us-east-1.amazonaws.com/locations?location=all' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
我已在API Gateway配置了CORS,但问题仍未解决,请问是否遗漏了某些配置?
解决方案
以下是几个可能导致问题的原因及修复步骤:
1. 必须处理OPTIONS预检请求
浏览器发起跨域GET请求前,会先发送OPTIONS预检请求确认服务器允许跨域。你的Lambda当前只处理了符合条件的GET请求,未处理OPTIONS请求,导致预检响应缺少CORS头,触发错误。
修改Lambda函数,优先处理OPTIONS请求,并确保所有响应分支都返回CORS头:
import axios from 'axios'; export const handler = async (event) => { // 优先处理OPTIONS预检请求 if (event.requestContext.http.method === 'OPTIONS') { return { statusCode: 200, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify('OK') }; } try { console.log('EVENT'); console.log(JSON.stringify(event, null, 2)); if (event.rawPath === '/locations') { if (event.rawQueryString === 'location=all') { const countriesResponse = await axios.get('https://countriesnow.space/api/v0.1/countries/states'); const countryNames = countriesResponse.data.data.map(country => country.name); return { statusCode: 200, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify(countryNames), }; } else { return { statusCode: 400, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify({ message: 'Invalid endpoint' }) }; } } else { return { statusCode: 400, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify({ message: 'Invalid endpoint' }) }; } } catch (error) { console.error('Error:', error); return { statusCode: 500, headers: { "Access-Control-Allow-Headers": "Content-Type", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "OPTIONS,POST,GET" }, body: JSON.stringify({ message: 'Internal Server Error' }) }; } };
2. API Gateway配置后需重新部署
即使在API Gateway控制台配置了CORS,必须重新部署API才能让配置生效:
- 进入API Gateway控制台,选择目标API
- 点击右上角「部署API」
- 选择已有的部署阶段(如prod),确认部署
3. 检查API Gateway集成响应的CORS头
如果API Gateway使用了集成响应,需确保集成响应中包含CORS相关头,或确认Lambda返回的头能被API Gateway正确传递。
4. 修复前端代码的逻辑错误
你的Lambda返回的是纯国家名称数组,但前端代码尝试访问data.locations,会导致后续报错。修改前端代码直接遍历返回的数组:
function fetchCountries() { fetch("https://AWS-ID.execute-api.us-east-1.amazonaws.com/locations?location=all") .then(response => { if (!response.ok) { console.log(response) throw new Error(`HTTP error! Status: ${response.status}`); } return response.json(); }) .then(data => { console.log("Fetched countries:", data); const countryDropdown = document.getElementById("country"); // 清空现有选项(保留默认项) countryDropdown.innerHTML = '<option value="">Select</option>'; // 遍历返回的国家数组 data.forEach(countryName => { const option = document.createElement("option"); option.value = countryName; option.textContent = countryName; countryDropdown.appendChild(option); }); }) .catch(error => console.error('Error fetching countries:', error)); }
若需要国家代码,可修改Lambda返回包含名称和代码的对象:
const countryData = countriesResponse.data.data.map(country => ({ name: country.name, code: country.iso2 // 假设接口返回iso2作为国家代码 }));
内容的提问来源于stack exchange,提问作者ENV

