【问题标题】:Zeit (Vercel) Now serverless authenticated requests failing because of CORSZeit (Vercel) 现在由于 CORS 导致无服务器身份验证请求失败
【发布时间】:2021-09-11 06:00:04
【问题描述】:

在执行 PATCH/POST/PUT 请求时,我无法正确处理 CORS 问题,从浏览器发送带有 Bearer tokenAuthorization 标头(这在浏览器之外可以正常工作对于GET 请求)在Zeit Now serverless 中。

如果有帮助,我将使用Auth0 作为授权方。


这是我的now.json 标题部分,我尝试了很多组合,但都没有从浏览器成功。


  1. 我尝试使用 npm cors 包没有成功
  2. 尝试在now.json 中添加routes
  3. 尝试使用res.addHeader() 在无服务器函数顶部设置标头
  4. 还尝试手动处理 OPTIONS 请求,对此进行变体:

最后,这是我得到的错误

Access to XMLHttpRequest at 'https://api.example.org/api/users' from origin 'https://example.org' has been blocked by CORS policy: 
Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

不知道我错了什么或如何正确处理。

【问题讨论】:

  • 完整的 now.json 是什么样的?
  • 在 index.js 中使用 app.use() 添加中间件
    需要示例吗?关注this

标签: javascript cors authorization serverless vercel


【解决方案1】:

我可以使用 micro-cors 绕过这个问题。

我检查了its code,它与我手动使用res.setHeader 自己尝试做的事情并没有太大区别,我猜可能错过了一些东西。

但我不明白为什么now.json 中的设置无法正常工作,我需要在无服务器功能中手动执行此操作。

无论如何,如果其他人发现这篇文章,我最终会得到这样的结果:

import micro from "micro-cors";

function MyApi(req, res) {
  if (req.method === "OPTIONS") {
    return res.status(200).end();
  }
  // handling other requests normally after this
}

const cors = micro();

export default cors(MyApi);

我可能会用自己编写的解决方案再试一次,以便更好地了解出了什么问题,也因为我不想要额外的依赖。

如果我这样做,将更新此答案。


编辑:经过深入检查后,我发现另一个问题是库 express-jwtjwt 解析失败时专门更改了 res 对象。

我有一个小型中间件,它正在破坏一切:

await authValidateMiddleware(req, res);

await 失败时,它打破了一切,因为express-jwt 在不知不觉中更改了res 标头(设置错误),然后我尝试手动设置res 标头以尝试正确处理错误我自己,因此抛出了关于“不止一次更改res 标头”的问题

【讨论】:

  • 您有其他解决方案吗?我也遇到了 cors 的问题。
  • @keisaac 您在使用任何快速中间件吗?对于身份验证或类似的东西?如果是这样,这些中间件也会更改 res 对象,因此您可能需要解决这些问题
  • 我没有使用 express,我正在按照 Vercel 的指南使用助手。我最终使用了 setHeader,但如果我试图访问正文,我遇到了另一个问题。
【解决方案2】:

我在 CORS 和 Vercel 无服务器功能方面遇到了非常相似的问题。

经过大量尝试→失败过程,我才找到了解决方案。


解决方案

tldr

最简单的解决方案,只使用micro-cors

并且有一个类似的实现;

import { NowRequest, NowResponse } from '@now/node';
import microCors from 'micro-cors';

const cors = microCors();

const handler = (request: NowRequest, response: NowResponse): NowResponse => {
  if (request.method === 'OPTIONS') {
    return response.status(200).send('ok');
  }

  // handle incoming request as usual
};

export default cors(handler);

更长的版本,但没有任何新的依赖

使用vercel.json 处理请求标头

vercel.json

{
  "headers": [
    {
      "source": "/.*",
      "headers": [
        {
          "key": "Access-Control-Allow-Origin",
          "value": "*"
        },
        {
          "key": "Access-Control-Allow-Headers",
          "value": "X-Requested-With, Access-Control-Allow-Origin, X-HTTP-Method-Override, Content-Type, Authorization, Accept"
        },
        {
          "key": "Access-Control-Allow-Credentials",
          "value": "true"
        }
      ]
    }
  ]
}

自行尝试后,上述设置中有2个重要的键

  1. 你必须将Access-Control-Allow-Origin设置为你想要的
  2. Access-Control-Allow-Headers 中,您必须在其值中包含 Access-Control-Allow-Origin

那么在无服务器功能中,你还需要处理pre-flight request

/api/index.ts

const handler = (request: NowRequest, response: NowResponse): NowResponse => {
  if (request.method === 'OPTIONS') {
    return response.status(200).send('ok');
  }

  // handle incoming request as usual
};

我建议通读micro-cors中的代码,非常简单的代码,你可以在几分钟内理解它的作用,这让我不必担心将它添加到我的依赖项中。

【讨论】:

    【解决方案3】:

    我遇到了类似的问题,通过将标头添加到路由中解决了这个问题,如下所示:

    "routes": [
        {
          "src": ".*",
          "methods": ["GET", "POST", "OPTIONS"],
          "headers": {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
            "Access-Control-Allow-Credentials": "true"
          },
          "dest": "index.js",
          "continue": true
        },
        {
          "src": "/user/login", "methods": ["POST"], "dest": "index.js"
        }
      ]
    

    记得加continue: true

    https://github.com/super-h-alt/zeit-now-cors-problems/blob/master/now.json

    【讨论】:

    • 这也能用axios实现吗?还是必须是 JSON 文件?
    • 谢谢!标头似乎已发送,但现在每当发出发布请求时,我都会收到 500 错误。你介意在codepen.io/mattfrancis888/pen/vYNPRZW查看我的 now.json 和 server.js 吗?
    【解决方案4】:

    我的情况几乎相同。我在 Vercel(现在)中有几个无服务器功能,我希望它们可供任何来源的任何人使用。我解决的方式类似于@illiteratewriter's answer

    首先,我的项目根目录中有以下now.json

    {
      "routes": [
        {
          "src": "/api/(.*)",
          "headers": {
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
            "Access-Control-Allow-Credentials": "true"
          },
          "continue": true
        },
        {
          "src": "/api/(.*)",
          "methods": ["OPTIONS"],
          "dest": "/api/cors"
        }
      ]
    }
    

    以下是两种路线配置的细分:

    {
      "src": "/api/(.*)",
      "headers": {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept",
        "Access-Control-Allow-Credentials": "true"
      },
      "continue": true
    }
    
    • "src": "/api/(.*)"

    匹配发往/api/*的任何请求。

    • "headers": [...]

    将 CORS 标头应用于路由,表示允许 CORS。

    • "continue": true

    在应用 CORS 标头后继续寻找其他路由匹配。这允许我们将 CORS 标头应用于 所有 路由,而不必针对每个路由进行。例如,现在 /api/auth/login/api/main/sub/resource 都将应用 CORS 标头。

    {
      "src": "/api/(.*)",
      "methods": ["OPTIONS"],
      "dest": "/api/cors"
    }
    

    此配置的作用是拦截所有 HTTP/OPTIONS 请求,这是 CORS 飞行前检查,并将它们重新路由到位于 /api/cors 的特殊处理程序。

    路由配置分解的最后一点将我们引向/api/cors.ts 函数。处理程序如下所示:

    import {NowRequest, NowResponse} from '@now/node';
    
    export default (req: NowRequest, res: NowResponse) => {
      return res.status(200).send();
    }
    

    这个处理程序所做的基本上是接受CORS pre-flight OPTIONS 请求并用200/OK 响应它,向客户端指示“是的,我们对CORS 业务开放。”

    【讨论】:

    • 是否必须创建 now.JSON?这可以通过 axios 完成吗?
    • @MatthewFrancis 我不确定你用 axios 做这件事是什么意思。你能详细说明一下吗?我只使用 axios 作为客户端库来发出 HTTP 请求。这里是关于服务器端代码以及接收和处理此类请求的全部内容。但是,如果您想为 Vercel 无服务器功能配置 CORS,那么创建 now.json 是唯一的方法(目前我知道)。
    【解决方案5】:

    接受的答案对我不起作用。然而 vercel 现在似乎有updated their advice,他们的示例代码是:

    const allowCors = fn => async (req, res) => {
      res.setHeader('Access-Control-Allow-Credentials', true)
      res.setHeader('Access-Control-Allow-Origin', '*')
      // another option
      // res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
      res.setHeader('Access-Control-Allow-Methods', 'GET,OPTIONS')
      res.setHeader(
        'Access-Control-Allow-Headers',
        'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
      )
      if (req.method === 'OPTIONS') {
        res.status(200).end()
        return
      }
      return await fn(req, res)
    }
    
    const handler = (req, res) => {
      const d = new Date()
      res.end(d.toString())
    }
    
    module.exports = allowCors(handler)
    

    值得一提的是,我并不完全确定 res.endres.send 之间的区别,但为了将响应实际接收到我的前端 (React),我将 handler 函数更改为:

    const handler = (req, res) => {
            const d = {data: "Hello World"}; 
            res.send(d)
    }
    

    这让我可以在 React 中摄取:

    function getAPIHelloWorld () {
        let connectStr = "/api"
        fetch(connectStr)
            .then(response => response.json())
            .then(response => {console.log(response.data)})
            .catch(err => console.error(err))
    }
    

    【讨论】:

      【解决方案6】:

      所以我遇到了同样的问题,通过在 vercel.json 中应用以下代码对我有用

      {
      "version": 2,
        "builds": [
          {
            "src": "src/app.ts",
            "use": "@vercel/node"
          }
        ],
        "routes": [
          {
            "src": "/(.*)",
            "dest": "src/app.ts",
            "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"]
          }
        ]
      }
      

      我缺少 "OPTIONS""PATCH" 方法,添加它们后一切正常。

      要提一下,这就是我使用 cors 的方式,希望这个答案对某人有所帮助

      app.use(cors({ origin: ['http://localhost:3000', /\.regenci\.online$/], credentials: true }))
      

      【讨论】:

        猜你喜欢
        • 2018-02-25
        • 2020-09-06
        • 2016-06-18
        • 2015-03-13
        • 2016-02-27
        • 2014-04-06
        • 1970-01-01
        • 2016-05-17
        • 1970-01-01
        相关资源
        最近更新 更多