修复 CORS "Response to preflight..." header 不存在于 AWS API 网关和放大

Fix CORS "Response to preflight..." header not present with AWS API gateway and amplify

我一直在为以下错误苦苦挣扎。我尝试了很多教程和 Whosebug 答案,none 的解决方案解决了我的问题。

Access to XMLHttpRequest at 'https://xxx' from origin 'http://localhost:3000' 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.

我正在使用 SAM 无服务器来创建我的 api。

template.yaml:

Globals:
  Function:
    Timeout: 10
  Api:
    Cors:
      AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
      AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
      AllowOrigin: "'*'"

我的 lambda 函数: 我的 GET 响应和 OPTIONS 响应都返回了以下 headers:

headers: {
  "Access-Control-Allow-Headers": "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
  "Access-Control-Allow-Origin": "'*'",
  "Access-Control-Allow-Methods": "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
}

我的 API 使用放大进入我的 ReactJs 应用程序:

API.get(apiName, path, {
   headers: {
      "Access-Control-Allow-Origin": "*",
      // "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,x-requested-with",
      // "Access-Control-Allow-Methods": "OPTIONS,POST,GET,PUT,DELETE",
      // 'Content-Type': 'application/json'
   }
})

我已经在我的 template.yaml、我的 lambda 函数和我的 reactJs 项目中尝试了 Access-Control-Allow-Headers、Access-Control-Allow-Methods 的所有组合。

这是我在 API 端点上调用 postman 中的选项时的结果。因此,我确实得到了正确的 headers,所以根据我的理解,我的 API 允许 CORS。

在与@Jannes Botis 进行了非常有帮助的讨论后,我找到了解决方案。

在 template.yaml 中,我将值更改为:

Globals:
  Function:
    Timeout: 10
  Api:
    Cors:
      AllowMethods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
      AllowHeaders: "'Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers'"
      AllowOrigin: "'*'"

  MyAPIFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: myendpoint/
      Handler: app.lambdaHandler
      Runtime: nodejs12.x
      Events:
        GetMyData:
          Type: Api
          Properties:
            RestApiId: !Ref MyApi
            Path: /myendpoint
            Method: get
        Options:
          Type: Api
          Properties:
            RestApiId: !Ref MyApi
            Path: /myendpoint
            Method: options
            Auth:
              ApiKeyRequired: false

注意: 您将收到错误消息“请求的资源上不存在 'xxx' header。”其中 xxx 是 Access-Control-Allow-Methods、Access-Control-Allow-Origin 和 Access-Control-Allow-Headers,因此您需要将它们添加到您的 AllowHeaders 中。另请注意,您必须使用 ApiKeyRequired: false.

添加 Options 资源

那么您的选项响应和获取请求应该具有相同的 headers:

headers: {
    "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,X-Amz-Security-Token,Authorization,X-Api-Key,X-Requested-With,Accept,Access-Control-Allow-Methods,Access-Control-Allow-Origin,Access-Control-Allow-Headers",
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT",
    "X-Requested-With": "*"
}

注意:'Accept' 必须存在,否则您将收到“对预检请求的响应未通过访问控制检查:它没有 HTTP 正常状态。”。

当您在 postman 中省略 x-api-key 时,您的预检必须能够通过 200 OK。

我将此回复包括在内,以防有人仍然因这个错误而烦恼。我花了一天时间试图解决这个问题——阅读我能做的关于 CORS 的一切,甚至将我的代码迁移到新的 AWS HttpAPI(无论如何这可能是一件好事)。 最重要的是,我一直关注错误消息:

Access to fetch at 'https://api.example.com/user/list' from origin 'http://localhost:8080' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

这可能会将您指向错误的方向。我的问题与“Access-Control-Allow-Origin”header 无关。事实上,我的问题的答案非常简单,我发送了一个 JWT 令牌作为“授权”header 的一部分,并且我没有在我的回复中包含“Access-Control-Allow-Headers”条目 - 呃! (但如果错误信息更准确就更好了...)

如果您正在阅读本文,您可能已经在网络上找到了许多很好的信息来源,但是这个网站对我非常有用:www.test-cors.org

如果有人将无服务器与 nodejs 一起使用,并面临这个带有 cors-policy 的 prelight 问题,这里是简单的解决方案 ...

functions:
  externalHandler:
    handler: handler.handlerExport
    events:
      - http:
          path: handlerPath
          method: post
          cors:
            origin: '*' # <-- Specify allowed origin
            headers: # <-- Specify allowed headers
              - Content-Type
              - X-Amz-Date
              - Accept
              - Authorization
              - X-Api-Key
              - X-Amz-Security-Token
              - X-Amz-User-Agent
            allowCredentials: false

在处理程序文件中

如果您使用的是快递

...
const app = express();
app.use(function (req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Credentials", true);
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
  res.header(
    "Access-Control-Allow-Headers",
    "x-www-form-urlencoded, Origin, X-Requested-With, Content-Type, Accept, Authorization"
  );
  next();
});
...

或一般

  const response = {
    statusCode: 200,
    headers: {
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Credentials': true,
    },
    body: JSON.stringify({
      product: product
    }),
  };

有关详细信息,请参阅 serverless article

如果您尝试访问不存在的 URL,也会发生此错误。哦。 在你陷入CORS地狱之前,首先确认请求URL确实是正确的,你想要访问的资源是有效的。

就我而言,我试图通过错误的 Lightsail URL 访问后端。向这个不正确的 URL 发送 POST 请求触发了 CORS 错误。使用正确的 URL 访问后端修复了错误。