avatar

cooyue

博客网站 - COOYUE

  • 首页
  • 友链
主页 什么是 Next.js Middleware?
文章

什么是 Next.js Middleware?

发表于 最近 更新于 最近
作者 Administrator
37~48 分钟 阅读

什么是 Next.js Middleware?

Next.js Middleware(在 v16.0.0 后更名为 Proxy)是一个强大的功能,允许你在请求完成之前在服务器上运行代码。基于传入的请求,你可以通过重写、重定向、修改请求或响应头,或直接响应来修改响应。

Middleware 在路由渲染之前执行,特别适合实现自定义服务器端逻辑,如身份验证、日志记录或处理重定向。

基础配置

1. 创建 Middleware 文件

在项目根目录(或 src 目录内)创建 middleware.ts 或 middleware.js 文件,与 pages 或 app 目录同级。

```typescript
// middleware.ts
import { NextResponse, NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}

export const config = {
matcher: '/about/:path*',
}
```

2. 基本结构

Middleware 文件必须导出:

  • middleware 函数:处理请求的主函数
  • config 对象(可选):配置 middleware 应用的路径

Matcher 配置详解

单路径匹配

```javascript
export const config = {
matcher: '/about'
}
```

多路径匹配

```javascript
export const config = {
matcher: ['/about', '/contact', '/dashboard/:path*']
}
```

使用正则表达式

排除特定路径:

```javascript
export const config = {
matcher: [
// 排除 API 路由、静态文件、图片优化和 .png 文件
'/((?!api|_next/static|_next/image|.\\.png$).)',
],
}
```

高级 Matcher 配置

```javascript
export const config = {
matcher: [
{
source: '/api/:path*',
locale: false, // 忽略基于语言环境的路由
has: [
{ type: 'header', key: 'Authorization', value: 'Bearer Token' },
{ type: 'query', key: 'userId', value: '123' },
],
missing: [
{ type: 'cookie', key: 'session', value: 'active' }
],
},
],
}
```

Matcher 路径模式规则

  1. 必须以 / 开头
  2. 命名参数:/about/:path 匹配 /about/a 和 /about/b
  3. 修饰符:
    • * = 零个或多个:/about/:path* 匹配 /about/a/b/c
    • ? = 零个或一个
    • + = 一个或多个
  4. 正则表达式:/about/(.*) 等同于 /about/:path*
  5. 锚定到路径开头:/about 匹配 /about 和 /about/team,但不匹配 /blog/about

常见使用场景

1. 身份验证

```typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
const token = request.cookies.get('auth-token')

if (!token) {
return NextResponse.redirect(new URL('/login', request.url))
}

return NextResponse.next()
}

export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*']
}
```

2. 国际化重定向

```typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
const country = request.geo?.country || 'US'

if (country === 'CN') {
return NextResponse.redirect(new URL('/zh-cn', request.url))
}

if (country === 'JP') {
return NextResponse.redirect(new URL('/ja', request.url))
}

return NextResponse.next()
}
```

3. A/B 测试

```typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
const bucket = request.cookies.get('bucket')

if (!bucket) {
const newBucket = Math.random() < 0.5 ? 'a' : 'b'
const response = NextResponse.next()
response.cookies.set('bucket', newBucket)
return response
}

if (bucket.value === 'b') {
return NextResponse.rewrite(new URL('/experiment-b', request.url))
}

return NextResponse.next()
}
```

4. API 限流

```typescript
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

const rateLimit = new Map()

export function middleware(request: NextRequest) {
const ip = request.ip || 'unknown'
const now = Date.now()
const windowMs = 60000 // 1 分钟
const maxRequests = 10

const requests = rateLimit.get(ip) || []
const recentRequests = requests.filter((time: number) => now - time < windowMs)

if (recentRequests.length >= maxRequests) {
return new NextResponse('Too Many Requests', { status: 429 })
}

recentRequests.push(now)
rateLimit.set(ip, recentRequests)

return NextResponse.next()
}

export const config = {
matcher: '/api/:path*'
}
```

5. 日志记录

```typescript
import { NextResponse } from 'next/server'
import type { NextRequest, NextFetchEvent } from 'next/server'

export function middleware(request: NextRequest, event: NextFetchEvent) {
// 使用 waitUntil 在后台记录日志
event.waitUntil(
fetch('https://analytics.example.com/log', {
method: 'POST',
body: JSON.stringify({
pathname: request.nextUrl.pathname,
timestamp: new Date().toISOString(),
userAgent: request.headers.get('user-agent'),
}),
})
)

return NextResponse.next()
}
```

Cookie 操作

读取 Cookie

```typescript
export function middleware(request: NextRequest) {
// 获取单个 cookie
const cookie = request.cookies.get('session')
console.log(cookie) // { name: 'session', value: 'abc123', Path: '/' }

// 获取所有 cookies
const allCookies = request.cookies.getAll()

// 检查 cookie 是否存在
const hasSession = request.cookies.has('session')

return NextResponse.next()
}
```

设置 Cookie

```typescript
export function middleware(request: NextRequest) {
const response = NextResponse.next()

// 简单设置
response.cookies.set('theme', 'dark')

// 详细设置
response.cookies.set({
name: 'session',
value: 'abc123',
path: '/',
maxAge: 3600,
httpOnly: true,
secure: true,
sameSite: 'strict'
})

return response
}
```

删除 Cookie

```typescript
export function middleware(request: NextRequest) {
const response = NextResponse.next()

// 删除单个 cookie
response.cookies.delete('session')

// 清除所有 cookies
request.cookies.clear()

return response
}
```

Header 操作

设置请求 Header

```typescript
export function middleware(request: NextRequest) {
// 克隆请求 headers
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-custom-header', 'my-value')
requestHeaders.set('x-user-ip', request.ip || 'unknown')

// 传递给下游
return NextResponse.next({
request: {
headers: requestHeaders,
},
})
}
```

设置响应 Header

```typescript
export function middleware(request: NextRequest) {
const response = NextResponse.next()

// 设置响应 header
response.headers.set('x-custom-header', 'my-value')
response.headers.set('x-response-time', Date.now().toString())

return response
}
```

CORS 配置

```typescript
import { NextRequest, NextResponse } from 'next/server'

const allowedOrigins = ['https://example.com', 'https://app.example.com']

const corsOptions = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
}

export function middleware(request: NextRequest) {
const origin = request.headers.get('origin') ?? ''
const isAllowedOrigin = allowedOrigins.includes(origin)

// 处理预检请求
if (request.method === 'OPTIONS') {
const preflightHeaders = {
...(isAllowedOrigin && { 'Access-Control-Allow-Origin': origin }),
...corsOptions,
}
return NextResponse.json({}, { headers: preflightHeaders })
}

// 处理普通请求
const response = NextResponse.next()

if (isAllowedOrigin) {
response.headers.set('Access-Control-Allow-Origin', origin)
}

Object.entries(corsOptions).forEach(([key, value]) => {
response.headers.set(key, value)
})

return response
}

export const config = {
matcher: '/api/:path*',
}
```

条件路由

基于路径的条件

```typescript
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl

if (pathname.startsWith('/admin')) {
// 管理员路由逻辑
return checkAdminAuth(request)
}

if (pathname.startsWith('/api')) {
// API 路由逻辑
return handleApiRequest(request)
}

if (pathname.startsWith('/blog')) {
// 博客路由逻辑
return NextResponse.rewrite(new URL(`/posts${pathname}`, request.url))
}

return NextResponse.next()
}
```

基于用户角色的路由

```typescript
export function middleware(request: NextRequest) {
const userRole = request.cookies.get('role')?.value
const { pathname } = request.nextUrl

// 管理员专属路由
if (pathname.startsWith('/admin') && userRole !== 'admin') {
return NextResponse.redirect(new URL('/unauthorized', request.url))
}

// VIP 专属内容
if (pathname.startsWith('/premium') && userRole !== 'vip') {
return NextResponse.redirect(new URL('/upgrade', request.url))
}

return NextResponse.next()
}
```

重写和重定向

重写(Rewrite)

重写会改变 URL 的内部处理,但浏览器地址栏不变:

```typescript
export function middleware(request: NextRequest) {
// 将 /old-blog/post-1 重写为 /blog/post-1
if (request.nextUrl.pathname.startsWith('/old-blog')) {
const newPath = request.nextUrl.pathname.replace('/old-blog', '/blog')
return NextResponse.rewrite(new URL(newPath, request.url))
}

return NextResponse.next()
}
```

重定向(Redirect)

重定向会改变浏览器地址栏的 URL:

```typescript
export function middleware(request: NextRequest) {
// 永久重定向(301)
if (request.nextUrl.pathname === '/old-page') {
return NextResponse.redirect(new URL('/new-page', request.url), 301)
}

// 临时重定向(302,默认)
if (request.nextUrl.pathname === '/temp') {
return NextResponse.redirect(new URL('/temporary-page', request.url))
}

return NextResponse.next()
}
```

直接响应

从 Next.js v13.1.0 开始,可以直接从 middleware 返回响应:

```typescript
export function middleware(request: NextRequest) {
const isAuthenticated = checkAuth(request)

if (!isAuthenticated) {
return Response.json(
{ success: false, message: 'Authentication failed' },
{ status: 401 }
)
}

return NextResponse.next()
}

export const config = {
matcher: '/api/:path*',
}
```

执行顺序

Middleware 会在每个路由上调用,执行顺序如下:

  1. next.config.js 中的 headers
  2. next.config.js 中的 redirects
  3. Middleware(rewrites、redirects 等)
  4. next.config.js 中的 beforeFiles(rewrites)
  5. 文件系统路由(public/、_next/static/、pages/、app/ 等)
  6. next.config.js 中的 afterFiles(rewrites)
  7. 动态路由(/blog/[slug])
  8. next.config.js 中的 fallback(rewrites)

高级配置

跳过尾部斜杠重定向

```javascript
// next.config.js
module.exports = {
skipTrailingSlashRedirect: true,
}
```

```javascript
// 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(/\.[\w]+/)) { return NextResponse.redirect( new URL(\`\{req.nextUrl.pathname}/`, req.nextUrl)
)
}
}
```

跳过 URL 规范化

```javascript
// next.config.js
module.exports = {
skipMiddlewareUrlNormalize: true,
}
```

性能优化建议

1. 精确的 Matcher

使用精确的 matcher 避免不必要的执行:

```javascript
// ❌ 不好 - 会在所有路由上执行
export const config = {
matcher: '/:path*'
}

// ✅ 好 - 只在需要的路由上执行
export const config = {
matcher: ['/api/:path*', '/dashboard/:path*']
}
```

2. 早期返回

尽早返回,避免不必要的处理:

```typescript
export function middleware(request: NextRequest) {
// 快速路径 - 静态资源直接通过
if (request.nextUrl.pathname.startsWith('/_next')) {
return NextResponse.next()
}

// 其他逻辑...
}
```

3. 避免重量级操作

Middleware 应该快速执行,避免:

  • 复杂的数据库查询
  • 大量的外部 API 调用
  • 重量级的计算

使用 waitUntil 处理后台任务:

```typescript
export function middleware(request: NextRequest, event: NextFetchEvent) {
// 后台任务不会阻塞响应
event.waitUntil(
logAnalytics(request)
)

return NextResponse.next()
}
```

调试技巧

1. 添加日志

```typescript
export function middleware(request: NextRequest) {
console.log('Middleware executed for:', request.nextUrl.pathname)
console.log('Headers:', Object.fromEntries(request.headers))
console.log('Cookies:', request.cookies.getAll())

return NextResponse.next()
}
```

2. 添加调试 Header

```typescript
export function middleware(request: NextRequest) {
const response = NextResponse.next()

if (process.env.NODE_ENV === 'development') {
response.headers.set('x-middleware-executed', 'true')
response.headers.set('x-pathname', request.nextUrl.pathname)
}

return response
}
```

常见问题

1. Middleware 不执行

检查项:

  • 文件位置是否正确(项目根目录或 src 目录)
  • Matcher 配置是否正确
  • 是否有语法错误

2. 无限重定向

**原因:**重定向目标也匹配 matcher

解决:

```typescript
export function middleware(request: NextRequest) {
// ❌ 错误 - 会导致无限重定向
if (!isAuthenticated(request)) {
return NextResponse.redirect(new URL('/login', request.url))
}
}

export const config = {
matcher: '/:path*' // 包括 /login
}

// ✅ 正确 - 排除登录页
export const config = {
matcher: '/((?!login).*)'
}
```

3. Cookie 未设置

**原因:**忘记返回修改后的响应

解决:

```typescript
// ❌ 错误
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.cookies.set('theme', 'dark')
// 忘记返回 response
}

// ✅ 正确
export function middleware(request: NextRequest) {
const response = NextResponse.next()
response.cookies.set('theme', 'dark')
return response // 必须返回
}
```

最佳实践

  1. 使用 TypeScript:获得更好的类型安全和 IDE 支持
  2. 精确的 Matcher:只在需要的路由上执行
  3. 早期返回:尽快返回,避免不必要的处理
  4. 后台任务:使用 waitUntil 处理非关键任务
  5. 错误处理:添加 try-catch 避免 middleware 崩溃
  6. 测试:为 middleware 编写单元测试
  7. 文档:注释复杂的逻辑和业务规则

总结

Next.js Middleware 是一个强大的工具,可以在请求到达页面之前进行各种处理。通过合理配置 matcher 和使用适当的 API,你可以实现:

  • ✅ 身份验证和授权
  • ✅ 国际化和地理位置路由
  • ✅ A/B 测试和功能标志
  • ✅ API 限流和安全
  • ✅ 日志和分析
  • ✅ 重写和重定向
  • ✅ CORS 处理

记住:Middleware 应该快速执行,专注于路由和请求/响应修改,避免重量级操作。


相关资源:

  • Next.js Middleware 官方文档
  • NextRequest API
  • NextResponse API
OpenClaw
许可协议:  CC BY 4.0
分享

相关文章

3月 2, 2026

什么是 Next.js Middleware?

什么是 Next.js Middleware? Next.js Middleware(在 v16.0.0 后更名为 Proxy)是一个强大的功能,允许你在请求完成之前在服务器上运行代码。基于传入的请求,你可以通过重写、重定向、修改请求或响应头,或直接响应来修改响应。 Middleware 在路由渲染

2月 28, 2026

OpenClaw 飞书配置完全指南OpenClaw

什么是 OpenClaw 飞书集成? OpenClaw 是一个强大的 AI 助手框架,支持通过飞书机器人与用户交互。通过飞书集成,你可以: 在飞书私聊中与 AI 助手对话 在群组中 @机器人获取帮助 使用流式输出实时查看 AI 生成内容 管理文档、知识库等飞书资源 实现多 Agent 路由,不同用户

2月 28, 2026

OpenClaw:开源的多渠道 AI 智能助手网关

什么是 OpenClaw? OpenClaw 是一个自托管的网关系统,它能够将你喜欢的聊天应用(WhatsApp、Telegram、Discord、iMessage 等)连接到 AI 编码助手。你只需在自己的机器(或服务器)上运行一个 Gateway 进程,它就会成为你的消息应用和始终可用的 AI

下一篇

用 OpenClaw 做视频:从创意到发布OpenClaw的完整流程

上一篇

Nextjs 实现国际化翻译 - App Router 模式解决方案

最近更新

  • Nextjs 实现国际化翻译 - App Router 模式解决方案
  • 什么是 Next.js Middleware?
  • 用 OpenClaw 做视频:从创意到发布OpenClaw的完整流程
  • OpenClaw 企业微信配置完全指南OpenClaw
  • OpenClaw 飞书配置完全指南OpenClaw

热门标签

工程 Flutter React JavaScript 面经 OpenClaw 前端 负载均衡

目录

©2026 cooyue. 保留部分权利。

使用 Halo 主题 Chirpy