添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
cover

为 Next.js@14 中的 API Route 配置 CORS

本文的主要内容是简单介绍了 CORS 的相关内容及如何为 Next.js@14 中的 API Route 配置 CORS。

2023-11-29

heading

CORS 跨源资源共享

Cross-Origin Resource Sharing 跨源资源共享, 指的是服务器允许托管在其他域或其他来源上的 Web 页面向其发送 HTTP 请求的一种机制。要理解 CORS,首先需要了解 Same-Origin Policy 同源策略。

heading

Same-Origin Policy 同源策略

同源策略是一种 Web 安全策略,用于限制一个“源“的文档或者它加载的脚本与另一个“源“的资源进行交互。

所谓的“同源”指的是两个 URL 具有相同的主机(hostname),并且使用相同的协议和端口号。在 Web 应用中,跨源访问有以下这些限制:

列表 操作 是否可以跨源
跨源网络访问 跨源写操作 一般是被允许的
跨源资源嵌入 一般是被允许的
跨源读操作 一般是不被允许的
跨源脚本 API 访问 Window window.blur 方法
window.close 方法
window.focus 方法
window.postMessage 方法
只读属性 window.closed
只读属性 window.frames
只读属性 window.length
读/写属性 window.location
只读属性 window.opener
只读属性 window.parent
只读属性 window.self
只读属性 window.top
只读属性 window.window
Location location.replace 方法
只写属性 HTMLAnchorElement.href
跨源数据存储访问 Web Storage/IndexDB 以源进行分割,无法跨源访问
heading

CORS 的实现

CORS 需要浏览器和服务器相互配合才能实现。

heading

CORS HTTP Headers

跨源资源共享标准新增了一组 HTTP Header ,浏览器和服务器可以通过发送或响应这些 HTTP Header 实现跨域访问资源。

HTTP 头 添加方 说明 取值 备注
Access-Control-Allow-Origin 服务器 指示响应的资源是否可以被给定的来源共享 origin 或 * 如果 Access-Control-Allow-Credentials 为 true,则不能使用通配符 *
Access-Control-Allow-Credentials 服务器 指示当请求的凭证标记为 true 时,是否可以公开对该请求响应 true 或 false 当用在对 preflight 预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials
Access-Control-Allow-Headers 服务器 用在对预检请求的响应中,指示实际的请求中可以使用哪些 HTTP 标头 header-name[, header-name]*
Access-Control-Allow-Methods 服务器 指定对预检请求的响应中,哪些 HTTP 方法允许访问请求的资源 method[, method]*
Access-Control-Expose-Headers 服务器 通过列出标头的名称,指示哪些标头可以作为响应的一部分公开 header-name[, header-name]* 不在列表中的 header 无法通过 JavaScript 获取
Access-Control-Max-Age 服务器 指示预检请求的结果能被缓存多久 delta-seconds
Access-Control-Request-Headers 浏览器 用于发起一个预检请求,告知服务器正式请求会使用哪些 HTTP Headers field-name[, field-name]*
Access-Control-Request-Method 浏览器 用于发起一个预检请求,告知服务器正式请求会使用哪一种 HTTP Method method
Origin 浏览器 指示获取资源的请求是从什么源发起的 hostname Orign 中不包含 pathname;标头字段总是被发送。
heading

Preflight request 预检请求

浏览器可以通过预检请求向服务器确认是否支持 CORS:

  • 当浏览器发送非“简单请求”时,浏览器会自动发送一个预检请求(使用 OPTIONS 方法,并携带
    Access-Control-Request-Method
    Access-Control-Request-Headers
    头)向服务器确认是否可以发送该请求;
  • 如果服务器允许该请求,则会响应该预检请求(携带对应的
    Access-Control-*
    头);
  • 当浏览器收到该响应后,再发送正式的请求。
  • 所谓的“简单请求”指的是:

  • Request Method 为:GET、HEAD、POST;
  • 手动添加的 Request Headers 只有以下几种:Accept、Accept-Language、Content-Language、Content-Type、Range;
  • Content-Type Header 的 Value 为: text/plain 或 multipart/form-data 或 application/x-www-form-urlencoded;
  • Range Header 的 Value 使用简单格式,即:bytes=256-、bytes=127-255;
  • 使用 XMLHttpRequest 发送请求时,没有监听 XMLHttpRequest.upload 上的事件;
  • 请求中没有使用 ReadableStream 对象。
  • heading

    在 Next.js@14 中为 API Route 实现 CORS

    如上所述,CORS 是通过浏览器和服务器相互配合实现的。在现代浏览器中一般都实现了 CORS 规范 ,因此一般情况下只需要在服务器中进行响应的配置即可。

    在 Next.js@14 中有以下几种方式可以为 API Route 配置 CORS:

  • next.config.js
    中设置
    headers
    选项
  • const CORS_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: "*" /** @type {import('next').NextConfig} */ const nextConfig = { async headers() { return [ source: "/api/:path*", // 为访问 /api/** 的请求添加 CORS HTTP Headers headers: CORS_HEADERS source: "/specific", // 为特定路径的请求添加 CORS HTTP Headers, headers: CORS_HEADERS module.exports = nextConfig
    next.config.js
  • middleware
    中配置:
  • import { NextResponse } from "next/server"; export function middleware() { const resp = NextResponse.next() resp.headers.append('Access-Control-Allow-Credentials', "true"); resp.headers.append('Access-Control-Allow-Origin', '*'); resp.headers.append('Access-Control-Allow-Methods', 'GET,DELETE,PATCH,POST,PUT'); resp.headers.append('Access-Control-Allow-Headers', '*'); return res export const config = { matcher: '/api/:path*' };
    middleware.ts

    matcher
    的具体规则参考 官方文档
  • 如果只是针对某些特定的 Route 才需要配置 CORS,除了通过
    matcher
    指定外,还可以单独在 API Route 中进行配置,比如:
  • import { NextResponse } from "next/server"; const appendCORS = (resp: NextResponse) => { resp.headers.append("Access-Control-Allow-Credentials", "true"); resp.headers.append("Access-Control-Allow-Origin", "*"); resp.headers.append("Access-Control-Allow-Methods", "GET,DELETE,PATCH,POST,PUT"); resp.headers.append("Access-Control-Allow-Headers", "*"); return resp; const setContentType = (resp: NextResponse, body: string) => { resp.headers.set("Content-Type", "application/json; charset=UTF-8"); resp.headers.set("Content-Length", Buffer.from(body).byteLength + ""); return resp;