Skip to main content

中间件

中间件允许您在请求完成之前运行代码。 然后,基于传入的请求,您可以通过重写、重定向、修改请求或响应头,或直接响应来修改响应。

中间件在缓存内容和路由匹配之前运行。有关更多详细信息, 请参阅匹配路径

约定

在项目的根目录中使用 middleware.ts(或 .js) 文件来定义中间件。 例如,在与 pagesapp 相同的级别,或者如果适用,内部使用 src

示例

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// 如果在内部使用 await,则此函数可以标记为 `async`
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}

// 请查看下面的“匹配路径”以了解更多信息
export const config = {
matcher: '/about/:path*',
}

匹配路径

中间件将为项目中的每个路由调用。以下是执行顺序:

  1. 来自 next.config.jsheaders
  2. 来自 next.config.jsredirects
  3. 中间件(rewritesredirects等)
  4. 来自 next.config.jsbeforeFilesrewrites
  5. 文件系统路由(public/_next/static/pages/app/ 等)
  6. 来自 next.config.jsafterFilesrewrites
  7. 动态路由(/blog/[slug]
  8. 来自 next.config.jsfallbackrewrites

有两种定义中间件将在哪些路径上运行的方法:

  1. 自定义匹配器配置
  2. 条件语句

匹配器

matcher 允许您筛选要在特定路径上运行的中间件。

middleware.js
export const config = {
matcher: '/about/:path*',
}

您可以使用数组语法匹配单个路径或多个路径:

middleware.js
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}

matcher 配置允许使用完整的正则表达式,因此支持负向先行断言或字符匹配等匹配。 负向先行断言的示例可见于此:

middleware.js
export const config = {
matcher: [
/*
* 匹配除以下路径之外的所有请求路径:
* - api(API 路由)
* - _next/static(静态文件)
* - _next/image(图像优化文件)
* - favicon.ico(favicon 文件)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
note

matcher 的值需要是常量,以便在构建时进行静态分析。动态值,如变量,将被忽略。

配置的匹配器:

  • 必须以 / 开头
  • 可包含命名参数:/about/:path 匹配 /about/a/about/b,但不匹配 /about/a/c
  • 可对命名参数(以 : 开头)进行修改:/about/:path* 匹配 /about/a/b/c, 因为 * 是零个或多个。? 是零个或一个,+ 是一个或多个
  • 可使用括号括起的正则表达式:/about/(.*)/about/:path* 相同
  • 阅读有关 path-to-regexp 文档的更多详细信息。
note

出于向后兼容性考虑,Next.js 总是将 /public 视为 /public/index。 因此,/public/:path 的匹配器将匹配。

条件语句

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}

if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}

NextResponse

NextResponse API 允许您:

  • 将传入请求redirect到不同的 URL
  • 通过显示给定的 URL rewrite响应
  • 为 API 路由、getServerSidePropsrewrite目标设置请求头
  • 设置响应 cookies
  • 设置响应头

要从中间件生成响应,您可以:

  • rewrite到生成响应的路由(Page 或 Route Handler)
  • 直接返回一个 NextResponse。查看生成响应

使用 Cookies

Cookies 是常规头。在Request中,它们存储在 Cookie 头中。 在Response中,它们在 Set-Cookie 头中。 Next.js 通过 NextRequestNextResponse 上的 cookies 扩展提供了一种方便的方法来访问和操作这些 cookies。

  1. 对于传入请求,cookies 具有以下方法:getgetAllsetdelete cookies。 您可以使用 has 检查是否存在 cookie,使用 clear 删除所有 cookie。

  2. 对于输出响应,cookies 具有以下方法:getgetAllsetdelete

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
// 假设“Cookie:nextjs=fast”头在传入请求中存在
// 使用 `RequestCookies` API 从请求中获取 cookies
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]

request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false

// 使用 `ResponseCookies` API 在响应中设置 cookies
const response = NextResponse.next()


response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// 出站响应将具有 `Set-Cookie:vercel=fast;path=/test` 头。

return response
}

设置 Headers

您可以使用 NextResponse API 设置请求和响应头(设置请求头在 Next.js v13.0.0+ 中可用)。

middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
// 克隆请求头并设置新头 `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')

// 您还可以在 NextResponse.rewrite 中设置请求头
const response = NextResponse.next({
request: {
// 新的请求头
headers: requestHeaders,
},
})

// 设置新的响应头 `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
note

避免设置大型头, 因为这可能会导致 431 Request Header Fields Too Large 错误, 具体取决于您的后端 Web 服务器配置。

生成响应

您可以直接从中间件中返回 ResponseNextResponse 实例来直接响应(从 Next.js v13.1.0+ 开始可用)。

middleware.ts
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'

// 限制中间件路径以 `/api/` 开头
export const config = {
matcher: '/api/:function*',
}

export function middleware(request: NextRequest) {
// 调用我们的身份验证函数来检查请求
if (!isAuthenticated(request)) {
// 使用 JSON 响应指示错误消息
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}

waitUntilNextFetchEvent

NextFetchEvent 对象扩展了本机 FetchEvent对象, 并包括 waitUntil() 方法。

waitUntil() 方法接受一个 Promise 作为参数,并扩展中间件的生命周期,直到 Promise 结束。这对于在后台执行工作非常有用。

middleware.ts
import { NextResponse } from 'next/server'
import type { NextFetchEvent, NextRequest } from 'next/server'

export function middleware(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://my-analytics-platform.com', {
method: 'POST',
body: JSON.stringify({ pathname: req.nextUrl.pathname }),
})
)

return NextResponse.next()
}

高级中间件标志

在 Next.js v13.1 中, 引入了两个用于中间件的附加标志, skipMiddlewareUrlNormalizeskipTrailingSlashRedirect, 用于处理高级用例。

skipTrailingSlashRedirect 允许禁用 Next.js 默认的添加或删除尾随斜杠的重定向, 允许在中间件内自定义处理,从而允许保留某些路径的尾随斜杠,但不允许保留其他路径的斜杠, 从而更容易进行渐进式迁移。

next.config.js
module.exports = {
skipTrailingSlashRedirect: true,
}
middleware.js
const legacyPrefixes = ['/docs', '/blog']

export default async function middleware(req) {
const { pathname } = req.nextUrl

if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) {
return NextResponse.next()
}

// 应用尾随斜杠处理
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}

skipMiddlewareUrlNormalize 允许禁用 Next.js 执行的 URL 规范化, 以使直接访问和客户端转换的处理相同。 在一些高级情况下,您需要使用原始 URL 进行完全控制,这个标志允许这样做。

next.config.js
module.exports = {
skipMiddlewareUrlNormalize: true,
}
middleware.js
export default async function middleware(req) {
const { pathname } = req.nextUrl

// GET /_next/data/build-id/hello.json

console.log(pathname)
// 使用该标志,现在这是 /_next/data/build-id/hello.json
// 没有该标志,这将被规范化为 /hello
}

运行时

中间件目前仅支持 Edge 运行时。 不能使用 Node.js 运行时。

版本历史

  • v13.1.0:引入了高级中间件标志
  • v13.0.0:中间件可以修改请求头、响应头,并发送响应
  • v12.2.0:中间件稳定,请参阅升级指南
  • v12.0.9:在 Edge 运行时强制使用绝对 URL(PR)
  • v12.0.0:添加中间件(Beta)