查看: 44|回覆: 0

在 Next.js 中使用 CORS 处理跨域请求

[複製鏈接]

5

主題

0

回帖

0

積分

热心网友

金币
0
閲讀權限
220
精華
0
威望
0
贡献
0
在線時間
0 小時
註冊時間
2010-10-26
發表於 2024-5-13 00:14:00 | 顯示全部樓層 |閲讀模式

What is CORS? 什么是CORS?

CORS is a security mechanism that enables a server to specify which origins are allowed to access and load resources in a web browser. In this context, an “origin” refers to the combination of the protocol, domain, and port number a request comes from.
CORS 是一种安全机制,它使服务器能够指定允许哪些源访问和加载 Web 浏览器中的资源。在此上下文中,“源”是指请求来自的协议、域和端口号的组合。

In detail, CORS is a protection system implemented by web browsers to enforce restrictions on cross-origin requests. Its main goal is to protect user security and prevent unauthorized access to resources and data.
具体来说,CORS 是由 Web 浏览器实现的保护系统,用于对跨域请求实施限制。其主要目标是保护用户安全并防止未经授权访问资源和数据。

To enforce CORS, browsers and servers exchange a set of specific HTTP headers.
为了强制实施 CORS,浏览器和服务器交换一组特定的 HTTP 标头。

Before making certain types of cross-origin requests, the browser sends a preflight request using the HTTP OPTIONS method to the server. The server then responds with some CORS headers, indicating whether the cross-origin should be permitted or denied.
在发出某些类型的跨域请求之前,浏览器会使用 HTTP OPTIONS 方法向服务器发送预检请求。然后,服务器使用一些 CORS 标头进行响应,指示是允许还是拒绝跨域。

If the preflight request is successful, the actual cross-origin request is performed by the browser. Otherwise, it gets blocked because of a CORS policy error.
如果预检请求成功,则浏览器将执行实际的跨域请求。否则,它将因 CORS 策略错误而被阻止。

The main CORS headers include:
主要的 CORS 标头包括:

  • Access-Control-Allow-Origin — 指定有权访问资源的源

  • Access-Control-Allow-Methods — 添加到预检响应中以指示允许的 HTTP 方法,例如 GET 、 POST 、 PUT 等。

  • Access-Control-Allow-Headers — 返回以响应预检请求,以指定当前请求中允许的 HTTP 标头

  • Access-Control-Allow-Credentials — 指示浏览器是否应在跨域请求中包含 cookie 或 HTTP 身份验证等凭据

CORS allows frontend applications to access resources from a different domain, ensuring secure cross-origin communication. By default, Next.js relies on a same-origin approach, imposing a strict policy. If you want to change that, you must configure it manually.
CORS 允许前端应用程序访问来自不同域的资源,从而确保安全的跨域通信。默认情况下,Next.js依赖于同源方法,并实施严格的策略。如果要更改它,则必须手动配置它。

Why you need CORS with Next.js 为什么需要带有 Next.js 的 CORS

Next.js has supported API development since v9.4. As mentioned earlier, APIs in Next.js do not involve CORS headers by default, following a same-origin policy instead. This means they can only be accessed in the browser by pages within the same domain.
Next.js 从 v9.4 开始支持 API 开发。如前所述,Next.js 中的 API 默认不涉及 CORS 标头,而是遵循同源策略。这意味着它们只能由同一域中的页面在浏览器中访问。

However, there are scenarios where you may want to access those endpoints from other origins. For example, if you had an application hosted on a different domain that needed to access those APIs in the frontend, it would be blocked by those cross-origin restrictions.
但是,在某些情况下,您可能希望从其他源访问这些终端节点。例如,如果在不同的域上托管了一个应用程序,该应用程序需要在前端访问这些 API,则该应用程序将受到这些跨域限制的阻止。

To avoid that issue, you need to enable CORS by configuring the appropriate headers on the Next.js server. This way, you can explicitly allow cross-origin requests for specific origins. Applications hosted on the trusted domains will then be able to make requests to your Next.js API routes.
若要避免此问题,需要通过在 Next.js 服务器上配置相应的标头来启用 CORS。这样,您可以显式允许对特定源的跨域请求。然后,托管在受信任域上的应用程序将能够向Next.js API 路由发出请求。


Prerequisites for learning to configure CORS in Next.js 学习在 Next.js 中配置 CORS 的先决条件

Before moving to the next section, be sure to meet the prerequisites below:
在转到下一部分之前,请务必满足以下先决条件:

Node.js v18+ installed on your machine
Node.js v18+ 安装在您的计算机上
A Next.js v13+ app
Next.js v13+ 应用程序
If your Next.js application is older than the recommended version, you can upgrade it by following the official migration guide.
如果您的Next.js应用程序低于推荐版本,您可以按照官方迁移指南进行升级。

To follow this tutorial, it does not matter if your project is based on the Pages Router or the new App Router. The proposed solutions will work in both scenarios.
要遵循本教程,无论您的项目是基于 Pages Router 还是新的 App Router,都无关紧要。建议的解决方案在这两种情况下都有效。

From now on, we will assume that all API routes in your Next.js project are under the /api path.
从现在开始,我们将假设您Next.js项目中的所有 API 路由都在 /api 路径下。

Enabling CORS for all API routes 为所有 API 路由启用 CORS

The most common approach to CORS is to set it up globally on all API endpoints. There are many ways to achieve that in Next.js. Here, you will explore three methods for using CORS in Next.js.
CORS 最常见的方法是在所有 API 端点上全局设置它。在Next.js中有很多方法可以实现这一目标。在这里,您将探讨在Next.js中使用 CORS 的三种方法。

Using the headers config 使用 headers 配置

You can manually set the CORS headers in Next.js thanks to the headers key in next.config.js:
您可以在 Next.js 中手动设置 CORS 标头,这要归功于 headers : next.config.js

// next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
    async headers() {
        return [
            {
                // matching all API routes
                source: "/api/:path*",
                headers: [
                    { key: "Access-Control-Allow-Credentials", value: "true" },
                    { key: "Access-Control-Allow-Origin", value: "*" }, // replace this your actual origin
                    { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" },
                    { key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
                ]
            }
        ]
    }
}

module.exports = nextConfig

headers() is an async function that must return an array of objects. When the incoming request matches the source path regex, the server will produce a response containing the custom HTTP headers.
headers() 是一个 async 必须返回对象数组的函数。当传入请求与 source 路径正则表达式匹配时,服务器将生成包含自定义 HTTP headers 的响应。

Note that the source must follow a specific syntax. If an incorrect path regex is used, Next.js will throw an Invalid header found error and fail with a source parse failed message.
请注意,必须 source 遵循特定的语法。如果使用不正确的路径正则表达式,Next.js将引发 Invalid header found 错误并失败并显示消息 source parse failed 。

Using a middleware 使用中间件

Next.js middleware enables you to perform specific operations before a request is completed. This also includes setting the CORS HTTP headers in the response. Based on your project structure, initialize a middleware.js file inside the pages, app, or the src folder as shown below:
Next.js中间件使您能够在请求完成之前执行特定操作。这还包括在响应中设置 CORS HTTP 标头。根据您的项目结构,初始化 pages 、 app 或 src 文件夹中的 middleware.js 文件,如下所示:

// src/middleware.js 
// or 
// src/app/middleware.js 
// or 
// src/pages/middleware.js 

import { NextResponse } from "next/server";

export function middleware() {
    // retrieve the current response
    const res = NextResponse.next()

    // add the CORS headers to the response
    res.headers.append('Access-Control-Allow-Credentials', "true")
    res.headers.append('Access-Control-Allow-Origin', '*') // replace this your actual origin
    res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT')
    res.headers.append(
        'Access-Control-Allow-Headers',
        'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
    )

    return res
}

// specify the path regex to apply the middleware to
export const config = {
    matcher: '/api/:path*',
}

Make sure to adapt the CORS policy specified in this snippet to your needs.
请确保根据需要调整此代码段中指定的 CORS 策略。

Note that the matcher config accepts a regex path, forcing the middleware to run only on specific paths. If an incorrect value is used, Next.js will fail and display an Invalid middleware found error message.
请注意, matcher 配置接受正则表达式路径,强制中间件仅在特定路径上运行。如果使用不正确的值,Next.js将失败并显示 Invalid middleware found 错误消息。

If your app relies on the App Router, you can remove the config export and place this file directly in the src/app/api path. In both cases, the middleware function will be applied to all API endpoints under the /api path.
如果您的应用依赖于 App Router,则可以删除导出并将 config 此文件直接放在路径中 src/app/api 。在这两种情况下,中间件函数都将应用于 /api 路径下的所有 API 端点。

Using a Vercel configuration file 使用 Vercel 配置文件

Chances are good that your Next.js project is hosted on Vercel, since Vercel is the development team behind Next.js. You can configure the CORS headers in a Next.js app deployed on Vercel by initializing a vercel.json file in the root folder of your project like so:
您的Next.js项目很有可能托管在 Vercel 上,因为 Vercel 是Next.js背后的开发团队。您可以通过初始化项目根文件夹中的 vercel.json 文件,在 Vercel 上部署的 Next.js 应用中配置 CORS 标头,如下所示:

{
  "headers": [
    {
      "source": "/api/(.*)",
      "headers": [
        { "key": "Access-Control-Allow-Credentials", "value": "true" },
        { "key": "Access-Control-Allow-Origin", "value": "*" },
        { "key": "Access-Control-Allow-Methods", "value": "GET,DELETE,PATCH,POST,PUT" },
        { "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
      ]
    }
  ]
}

As you can see, the headers key has a similar syntax to the equivalent one in next.config.js. What changes is the format of the source field.
如您所见,该 headers 键的语法与 中的 next.config.js 等效语法相似。更改的是 source 字段的格式。

Alternatively, you can also set up CORS with the following Vercel serverless function:
或者,您也可以使用以下 Vercel 无服务器函数设置 CORS:

const enableCors = fn => async (req, res) => {
  res.setHeader('Access-Control-Allow-Credentials', true)
  res.setHeader('Access-Control-Allow-Origin', '*') // replace this your actual origin
  res.setHeader('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT')
  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'
  )

  // specific logic for the preflight request
  if (req.method === 'OPTIONS') {
    res.status(200).end()
    return
  }

  return await fn(req, res)
}

const handler = (req, res) => {
  const currentDate = new Date()
  res.end(currentDate.toString())
}

module.exports = enableCors(handler)

Reading the ALLOW header values from the env variables 从 env 变量中 ALLOW 读取标头值

CORS policies may change over time. Thus, you should not hard-code them. Instead, you can set up some environment variables as shown below:
CORS 策略可能会随时间而更改。因此,您不应该对它们进行硬编码。相反,您可以设置一些环境变量,如下所示:

ACCESS_CONTROL_ALLOW_CREDENTIALS="true"
ACCESS_CONTROL_ALLOW_ORIGIN="*"
ACCESS_CONTROL_ALLOW_METHODS="GET,OPTIONS,PATCH,DELETE,POST,PUT"
ACCESS_CONTROL_ALLOW_HEADERS="X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version"

Then, use them in your code:
然后,在代码中使用它们:


res.headers.append('Access-Control-Allow-Credentials', process.env.ACCESS_CONTROL_ALLOW_CREDENTIALS)
res.headers.append('Access-Control-Allow-Origin', process.env.ACCESS_CONTROL_ALLOW_ORIGIN) // replace this your actual origin
res.headers.append('Access-Control-Allow-Methods', process.env.ACCESS_CONTROL_ALLOW_METHODS)
res.headers.append('Access-Control-Allow-Headers', process.env.ACCESS_CONTROL_ALLOW_HEADERS)

Great! The CORS definition logic no longer involves hard-coded strings.
伟大!CORS 定义逻辑不再涉及硬编码字符串。

Allowing multiple CORS origins 允许多个 CORS 源

The Access-Control-Allow-Origin header is a binary option that accepts either a single origin or all origins. You would use an asterisk * to set this header to accept all domains, but this wildcard cannot be used when credentials are included in the request.
Access-Control-Allow-Origin 标头是一个二进制选项,它接受单个源或所有源。您可以使用星号 * 将此标头设置为接受所有域,但当请求中包含凭据时,不能使用此通配符。

If you want to permit multiple but not all CORS origins, you need to write custom logic. Keep in mind that you can only implement this approach with the middleware solution. The other two options for enabling CORS in Next.js involve static files that do not accept custom behavior.
如果要允许多个但不是所有 CORS 源,则需要编写自定义逻辑。请记住,只能使用中间件解决方案实现此方法。在Next.js中启用 CORS 的另外两个选项涉及不接受自定义行为的静态文件。

Update the middleware.js file as below:
按如下方式更新 middleware.js 文件:

import { NextResponse } from "next/server";

// the list of all allowed origins
const allowedOrigins = [
  'http://localhost:3000', 
  'https://example-1.com', 
  'https://example-2.com', 
  // ...
  'https://example-99.com', 
];

export function middleware(req) {
    // retrieve the current response
    const res = NextResponse.next()

    // retrieve the HTTP "Origin" header 
    // from the incoming request
    req.headers.get("origin")

    // if the origin is an allowed one,
    // add it to the 'Access-Control-Allow-Origin' header
    if (allowedOrigins.includes(origin)) {
      res.headers.append('Access-Control-Allow-Origin', origin);
    }

    // add the remaining CORS headers to the response
    res.headers.append('Access-Control-Allow-Credentials', "true")
    res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT')
    res.headers.append(
        'Access-Control-Allow-Headers',
        'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version'
    )

    return res
}

// specify the path regex to apply the middleware to
export const config = {
    matcher: '/api/:path*',
}

Enabling CORS only for a specific endpoint 仅为特定终结点启用 CORS

Suppose you want to define a particular CORS policy for the /api/special-data endpoint. To do so, you could add a new entry to the array returned by headers() in next.config.js:
假设您要为 /api/special-data 终结点定义特定的 CORS 策略。为此,您可以向 in 返回 headers() next.config.js 的数组添加一个新条目:

// next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
    async headers() {
        return [
            {
                // matching all API routes
                source: "/api/:path*",
                headers: [
                   // omitted for brevity...
                ]
            },
            {
                source: "/api/special-data",
                headers: [
                    { key: "Access-Control-Allow-Credentials", value: "false" },
                    { key: "Access-Control-Allow-Origin", value: "https://example.com" },
                    { key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" },
                    { key: "Access-Control-Allow-Headers", value: "Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date" },
                ]
            }
        ]
    }
}

module.exports = nextConfig

Note that the source field of the second entry matches the endpoint of the desired API. This means that Next.js will prefer it to the more generic first entry.
请注意,第二个条目的 source 字段与所需 API 的端点匹配。这意味着Next.js更喜欢它而不是更通用的第一个条目。

Otherwise, you could update middleware.js and adapt its logic based on the request’s pathname:
否则,您可以根据请求的路径名更新 middleware.js 和调整其逻辑:

import { NextResponse } from "next/server";

export function middleware(req) {
    // retrieve the current response
    const res = NextResponse.next()

    // if the incoming is for the desired API endpoint
    if (req.nextUrl.pathname === '/special-data') {
        res.headers.append('Access-Control-Allow-Credentials', "false")
        res.headers.append('Access-Control-Allow-Origin', 'https://example.com') 
        res.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT')
        res.headers.append('Access-Control-Allow-Headers', 'Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date')
    } else {
        // generic CORS policy omitted for brevity....
    }

    return res
}

// specify the path regex to apply the middleware to
export const config = {
    matcher: '/api/:path*',
}

If you are an App Router user, you can get the same result by creating a middleware.js file in the src/app/special-data folder.
如果您是 App Router 用户,则可以通过在 src/app/special-data 文件夹中创建 middleware.js 文件来获得相同的结果。

Fantastic! The single-origin restriction is no longer an issue!
匪夷所思!单一来源限制不再是问题!



来源:https://www.cnblogs.com/-gjq/p/18188453
回覆

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 立即注册

本版積分規則

相关侵权、举报、投诉及建议等,请发 E-mail:qiongdian@foxmail.com

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部