6月2日 23:10
Next.js 中间件怎么用?认证重定向、A/B 测试和 Edge Runtime 限制
Next.js 中间件在请求到达页面之前执行,适合做认证检查、路由重写、A/B 测试等。它跑在 Edge Runtime 上,冷启动快但功能有限——不能用 Node.js API。
基本用法
在项目根目录创建 middleware.ts:
typescriptimport { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export function middleware(request: NextRequest) { // 请求到达页面之前执行 console.log(request.nextUrl.pathname); return NextResponse.next(); // 放行,继续处理请求 } // 限制中间件只对匹配的路径生效 export const config = { matcher: ['/dashboard/:path*', '/api/:path*'], };
matcher 很重要——不设的话每个请求(包括静态资源)都经过中间件,拖慢性能。
认证重定向
最常见的用例:未登录用户跳转到登录页:
typescriptexport function middleware(request: NextRequest) { const token = request.cookies.get('session-token'); if (!token) { const loginUrl = new URL('/login', request.url); loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname); return NextResponse.redirect(loginUrl); } return NextResponse.next(); } export const config = { matcher: ['/dashboard/:path*'], };
修改请求头
中间件可以给请求加 header,页面里用 headers() 读取:
typescriptexport function middleware(request: NextRequest) { const requestHeaders = new Headers(request.headers); requestHeaders.set('x-pathname', request.nextUrl.pathname); return NextResponse.next({ request: { headers: requestHeaders }, }); }
页面里:
typescriptimport { headers } from 'next/headers'; export default function Page() { const pathname = headers().get('x-pathname'); // ... }
A/B 测试
根据 Cookie 或随机分配给用户不同版本:
typescriptexport function middleware(request: NextRequest) { let variant = request.cookies.get('ab-variant')?.value; if (!variant) { variant = Math.random() > 0.5 ? 'A' : 'B'; } const response = NextResponse.next(); response.cookies.set('ab-variant', variant); // 重写到不同页面 if (variant === 'B' && request.nextUrl.pathname === '/pricing') { return NextResponse.rewrite(new URL('/pricing-b', request.url)); } return response; }
NextResponse.rewrite 在服务端切换到另一个页面,用户看到的 URL 不变。
限制:Edge Runtime
中间件跑在 Edge Runtime,不是 Node.js。这意味着:
- 不能用
fs、path、crypto(Node.js 内置模块) - 不能用
prisma、mongoose(数据库客户端) - 不能用
jsonwebtoken(需要 crypto) - 执行时间限制 30 秒(Vercel 上是 25 秒)
简单的 token 验证、Cookie 操作没问题。复杂的认证逻辑(查数据库验证 token)不应该放中间件——放在页面组件或 API 路由里。
常见问题
中间件死循环:中间件重定向到 /login,但 /login 也匹配了 matcher。解决:matcher 排除 /login,或在中间件里判断路径。
cookies 在 Server Component 里读不到:中间件 response.cookies.set() 设置的 Cookie 在同一请求的 Server Component 里读不到——因为中间件的 response 还没返回给浏览器。需要通过 request headers 传递。