从0死磕全栈之Next.js 中间件(Middleware)详解与实战

127 阅读3分钟

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)文件,与 apppages 同级:

/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 环境(如使用 fschild_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 特性(除非切换到 nodejs runtime)。
  • 静态导出(next export)不支持中间件
  • 路径匹配规则需以 / 开头

六、总结

Next.js 中间件为开发者提供了在请求生命周期早期介入的能力,是实现全局逻辑(如鉴权、国际化、A/B 测试)的理想工具。通过合理使用 matcherrewriteredirect、Cookie 和 Headers,你可以构建高性能、灵活的 Web 应用。