Next.js 的 中间件(Middleware) 是一个强大的功能,允许你在请求到达页面或 API 路由之前,在服务器端执行自定义逻辑。它非常适合用于实现身份验证、日志记录、A/B 测试、重定向、国际化等场景。
一、什么是 Next.js 中间件?
中间件是一个运行在 Edge Runtime(默认) 或 Node.js Runtime(v15.5+ 支持) 的函数,它在 路由渲染之前 执行。你可以:
- 修改请求头或响应头
- 重写(rewrite)URL
- 重定向(redirect)用户
- 直接返回响应(如 JSON 错误)
- 读取或设置 Cookie
- 实现 CORS、鉴权等逻辑
⚠️ 注意:中间件应尽量轻量,避免依赖共享模块或全局变量。因为中间件可能部署在 CDN 边缘节点上,用于快速处理重定向/重写。
二、如何创建中间件?
在项目根目录(或 src 目录下)创建 middleware.ts(或 .js)文件,与 app 或 pages 同级:
/my-next-app
├── src/
│ ├── middleware.ts ← 中间件文件
│ ├── app/
│ └── ...
└── ...
基本结构
import { NextRequest, NextResponse } from 'next/server'
export function middleware(request: NextRequest) {
// 中间件逻辑
return NextResponse.next() // 继续执行后续流程
}
// 可选:配置中间件生效的路径
export const config = {
matcher: '/about/:path*',
}
✅ 中间件函数可以是
default导出,也可以命名为middleware,但一个文件只能导出一个中间件函数。
三、核心功能详解
1. matcher 配置(路径匹配)
通过 config.matcher 控制中间件在哪些路径下运行:
export const config = {
matcher: [
'/dashboard/:path*', // 匹配 /dashboard 及其子路径
'/api/user/:id', // 匹配带参数的路径
'/((?!api|_next|favicon).*)' // 负向匹配:排除 api、静态资源等
]
}
- 支持通配符:
*(零或多个)、?(零或一个)、+(一个或多个) - 支持正则表达式:
/((?!api).*) - 支持高级条件匹配(
has/missing):
export const config = {
matcher: [
{
source: '/api/*',
has: [{ type: 'header', key: 'Authorization' }],
missing: [{ type: 'cookie', key: 'session' }]
}
]
}
📌
matcher必须是编译时常量,不能使用变量。
2. 重定向(Redirect)
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/login', request.url))
}
也可使用原生
Response.redirect()。
3. 重写(Rewrite)
不改变浏览器 URL,但渲染另一个页面:
if (request.nextUrl.pathname.startsWith('/old')) {
return NextResponse.rewrite(new URL('/new', request.url))
}
4. 操作 Cookie
Next.js 提供了便捷的 Cookie API:
export function middleware(request: NextRequest) {
// 读取 Cookie
const token = request.cookies.get('auth-token')?.value
// 设置响应 Cookie
const response = NextResponse.next()
response.cookies.set('theme', 'dark')
return response
}
5. 设置请求/响应头
export function middleware(request: NextRequest) {
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-user-role', 'admin')
const response = NextResponse.next({
request: { headers: requestHeaders }
})
response.headers.set('x-middleware-applied', 'true')
return response
}
⚠️ 注意:设置请求头要用
NextResponse.next({ request: { headers } }),而不是直接传headers。
6. 直接返回响应(v13.1+)
可用于 API 鉴权失败时直接返回错误:
export function middleware(request: NextRequest) {
if (!isValidToken(request)) {
return Response.json({ error: 'Unauthorized' }, { status: 401 })
}
}
7. 后台任务(waitUntil)
用于日志上报、埋点等异步操作:
export function middleware(req: NextRequest, event: NextFetchEvent) {
event.waitUntil(
fetch('https://analytics.example.com/log', {
method: 'POST',
body: JSON.stringify({ path: req.nextUrl.pathname })
})
)
return NextResponse.next()
}
8. 运行时切换(v15.5+)
默认使用 Edge Runtime,如需 Node.js 环境(如使用 fs、child_process):
export const config = {
runtime: 'nodejs'
}
四、实战:实现一个简单的登录鉴权中间件
需求
- 未登录用户访问
/dashboard时,重定向到/login - 已登录用户可正常访问
- 登录状态通过 Cookie 中的
auth-token判断
代码实现(src/middleware.ts)
import { NextRequest, NextResponse } from 'next/server'
// 模拟验证 token(实际项目中应调用 auth 服务)
function isAuthenticated(request: NextRequest): boolean {
const token = request.cookies.get('auth-token')?.value
return !!token && token === 'valid-token' // 简化判断
}
export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname
// 仅对 /dashboard 及其子路径生效
if (pathname.startsWith('/dashboard')) {
if (!isAuthenticated(request)) {
// 未登录,重定向到登录页
const loginUrl = new URL('/login', request.url)
loginUrl.searchParams.set('redirect', pathname) // 记住原始路径
return NextResponse.redirect(loginUrl)
}
}
// 其他路径正常通过
return NextResponse.next()
}
// 配置 matcher,只在 dashboard 路径下运行
export const config = {
matcher: '/dashboard/:path*',
}
五、注意事项
- 中间件对所有路由默认生效,务必使用
matcher精确控制范围。 - 避免在中间件中引入大型依赖,会影响 Edge 性能。
- 不要依赖
process.env以外的 Node.js 特性(除非切换到nodejsruntime)。 - 静态导出(
next export)不支持中间件。 - 路径匹配规则需以
/开头。
六、总结
Next.js 中间件为开发者提供了在请求生命周期早期介入的能力,是实现全局逻辑(如鉴权、国际化、A/B 测试)的理想工具。通过合理使用 matcher、rewrite、redirect、Cookie 和 Headers,你可以构建高性能、灵活的 Web 应用。