You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决AWS API Gateway集成Lambda的CORS跨域错误

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 14:58:14