介绍
Next.js 中间件在每个请求之前执行,在边缘或无服务器区域运行。项目根目录下的单个 middleware.ts 文件会拦截传入请求,并可以在请求到达匹配路由之前重定向、重写、操作标头或执行身份验证检查。
中间件非常适合在页面渲染之前进行快速、基于边缘的决策。默认情况下,它不在静态资源或 _next/static 上运行。
基本结构
中间件文件导出一个接收 NextRequest 并返回 NextResponse 的函数,以及定义哪些路径触发执行的 config 对象。
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
console.log(`Request: ${request.nextUrl.pathname}`);
return NextResponse.next();
}
export const config = {
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
};
matcher 配置使用带有负向前瞻的路径到正则表达式语法来排除静态文件。对于特定于部分的中间件,请使用多种模式,例如 ['/dashboard/:path*', '/admin/:path*']。
认证与授权
在中间件中处理身份验证优于每页检查,因为它提供了单一事实来源并消除了页面转换时的闪烁。
export function middleware(request: NextRequest) {
const token = request.cookies.get("session")?.value;
const { pathname } = request.nextUrl;
if (!token && pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
if (token && pathname.startsWith("/login")) {
return NextResponse.redirect(new URL("/dashboard", request.url));
}
return NextResponse.next();
}
对于基于角色的访问,请在允许访问管理路由之前解码 JWT 或会话令牌并检查用户的角色。 NextAuth.js、Clerk 和 Auth0 都提供了简化此过程的中间件助手。
请求重写和重定向
中间件支持两种类型的 URL 操作。重写可在不更改浏览器 URL 的情况下提供不同的内容,而重定向则完全更改 URL。
export function middleware(request: NextRequest) {
const { pathname, hostname } = request.nextUrl;
const tenant = hostname.split(".")[0];
if (tenant && tenant !== "www") {
request.nextUrl.pathname = `/${tenant}${pathname}`;
return NextResponse.rewrite(request.nextUrl);
}
}
常见用例包括本地化登陆页面、功能门控内容以及基于子域或 cookie 值的多租户路由。
地理位置和个性化
边缘中间件通过 request.geo 提供地理位置数据,包括城市、国家、地区、纬度和经度。 request.ip 属性给出客户端 IP 地址。
export function middleware(request: NextRequest) {
const { country } = request.geo;
const { pathname } = request.nextUrl;
if (["DE", "FR", "IT", "ES", "NL"].includes(country) && !pathname.startsWith("/gdpr")) {
return NextResponse.redirect(new URL("/gdpr", request.url));
}
return NextResponse.next();
}
区域定价、内容本地化和 GDPR 同意路由都是基于地理位置的中间件逻辑的实际应用。
边缘运行时限制
中间件运行在 Edge Runtime 上,它有重要的限制:
| 限制 | 详情 |
|---|---|
| Node.js API | 不可用(无文件系统、加密、路径) |
| 最大执行 | 30 秒(目标是 1 秒以内) |
| 捆绑尺寸 | 1 MB 限制(代码加依赖项) |
| 数据库 | 无直接联系;使用 API 路由 |
保持中间件逻辑精简。使用 API 路由或服务器组件进行繁重的计算、数据库访问或文件系统操作。
结论
使用中间件进行快速、基于边缘的决策,例如身份验证、重定向和 URL 重写。保持逻辑简单,因为它在每个匹配的请求上运行。为了性能,优先使用 matcher 配置而不是函数内部的条件检查。使用 Playwright 或 Cypress 等工具将中间件行为与页面逻辑分开测试。

