Zeit (Vercel) Now 无服务器身份验证请求因 CORS 而失败
Zeit (Vercel) Now serverless authenticated requests failing because of CORS
当浏览器发送 Authorization
[=53= 的 PATCH
/POST
/PUT
请求时,我无法正确处理 CORS 问题] 在 Zeit Now serverless.
中带有 Bearer token
(这在浏览器外部和 GET
请求中正常工作)
如果有帮助,我正在使用 Auth0 作为授权方。
这是我的 now.json
header 部分,我已经尝试了很多组合,但在浏览器中都没有成功。
- 我尝试使用 npm
cors
包但没有成功
- 试图在
now.json
中添加 routes
- 尝试使用
res.addHeader()
在无服务器函数的顶部设置 headers
- 还尝试手动处理
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.
不确定我错了什么或如何正确处理。
我能够使用 micro-cors 绕过这个问题。
我检查了 its code,它与我尝试通过手动使用 res.setHeader
自己尝试做的事情没有太大区别,我猜可能漏掉了一些东西。
然而,我不明白为什么 now.json
中的设置无法正常工作,我需要在无服务器功能中手动执行此操作。
无论如何,万一其他人发现这个 post,我最终得到这样的东西:
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);
我可能会用 self-written 解决方案再试一次,以便更好地理解哪里出了问题,也因为我不想要额外的依赖。
如果我这样做会更新这个答案。
编辑: 深入检查后,我发现另一个问题是库 express-jwt
专门更改 res
object 时jwt
解析失败。
我有一个小的中间件,它破坏了一切:
await authValidateMiddleware(req, res);
当 await
失败时,它破坏了所有内容,因为 express-jwt
在不知不觉中更改了 res
headers (设置错误)然后我尝试设置res
headers 自己手动尝试正确处理错误,因此抛出有关 "changing the res
headers more than once".
的问题
我遇到了类似的问题,通过向路由添加 header 解决了问题,如下所示:
"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
我的处境几乎相同。我在 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 headers 应用于路由,表示允许 CORS。
"continue": true
在应用 CORS headers 后继续寻找其他路由匹配项。这允许我们将 CORS headers 应用于 所有 路由,而不是必须这样做 per-route。例如,现在 /api/auth/login
和 /api/main/sub/resource
都将应用 CORS headers。
{
"src": "/api/(.*)",
"methods": ["OPTIONS"],
"dest": "/api/cors"
}
此配置所做的是拦截所有 HTTP/OPTIONS
请求,即 CORS pre-flight 检查,并将它们 re-route 到 /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
响应它,向客户端指示 "Yes, we are open for CORS business."
我对 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
处理请求headers
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个键重要,
- 您必须将
Access-Control-Allow-Origin
设置为您想要的
- 在
Access-Control-Allow-Headers
中,您 必须将 Access-Control-Allow-Origin
包含在其值中。
那么在serverless函数中,你还需要处理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 中的代码,这是非常简单的代码,您可以在几分钟内理解它会做什么,这让我不担心将其添加到我的依赖。
接受的答案对我不起作用。然而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.end
和 res.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))
}
所以我遇到了同样的问题,通过在 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 }))
当浏览器发送 Authorization
[=53= 的 PATCH
/POST
/PUT
请求时,我无法正确处理 CORS 问题] 在 Zeit Now serverless.
Bearer token
(这在浏览器外部和 GET
请求中正常工作)
如果有帮助,我正在使用 Auth0 作为授权方。
这是我的 now.json
header 部分,我已经尝试了很多组合,但在浏览器中都没有成功。
- 我尝试使用 npm
cors
包但没有成功 - 试图在
now.json
中添加 - 尝试使用
res.addHeader()
在无服务器函数的顶部设置 headers
- 还尝试手动处理
OPTIONS
请求,并执行以下变体:
routes
最后,这是我得到的错误
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.
不确定我错了什么或如何正确处理。
我能够使用 micro-cors 绕过这个问题。
我检查了 its code,它与我尝试通过手动使用 res.setHeader
自己尝试做的事情没有太大区别,我猜可能漏掉了一些东西。
然而,我不明白为什么 now.json
中的设置无法正常工作,我需要在无服务器功能中手动执行此操作。
无论如何,万一其他人发现这个 post,我最终得到这样的东西:
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);
我可能会用 self-written 解决方案再试一次,以便更好地理解哪里出了问题,也因为我不想要额外的依赖。
如果我这样做会更新这个答案。
编辑: 深入检查后,我发现另一个问题是库 express-jwt
专门更改 res
object 时jwt
解析失败。
我有一个小的中间件,它破坏了一切:
await authValidateMiddleware(req, res);
当 await
失败时,它破坏了所有内容,因为 express-jwt
在不知不觉中更改了 res
headers (设置错误)然后我尝试设置res
headers 自己手动尝试正确处理错误,因此抛出有关 "changing the res
headers more than once".
我遇到了类似的问题,通过向路由添加 header 解决了问题,如下所示:
"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
我的处境几乎相同。我在 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 headers 应用于路由,表示允许 CORS。
"continue": true
在应用 CORS headers 后继续寻找其他路由匹配项。这允许我们将 CORS headers 应用于 所有 路由,而不是必须这样做 per-route。例如,现在 /api/auth/login
和 /api/main/sub/resource
都将应用 CORS headers。
{
"src": "/api/(.*)",
"methods": ["OPTIONS"],
"dest": "/api/cors"
}
此配置所做的是拦截所有 HTTP/OPTIONS
请求,即 CORS pre-flight 检查,并将它们 re-route 到 /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
响应它,向客户端指示 "Yes, we are open for CORS business."
我对 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
处理请求headers
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个键重要,
- 您必须将
Access-Control-Allow-Origin
设置为您想要的 - 在
Access-Control-Allow-Headers
中,您 必须将Access-Control-Allow-Origin
包含在其值中。
那么在serverless函数中,你还需要处理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 中的代码,这是非常简单的代码,您可以在几分钟内理解它会做什么,这让我不担心将其添加到我的依赖。
接受的答案对我不起作用。然而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.end
和 res.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))
}
所以我遇到了同样的问题,通过在 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 }))