中间件
middleware.js|ts 文件用于编写中间件并在请求完成之前在服务器上运行代码。然后,基于传入的请求,你可以通过重写、重定向、修改请求或响应头,或直接响应来修改响应。
中间件在路由渲染之前执行。它对于实现自定义服务器端逻辑(如身份验证、日志记录或处理重定向)特 别有用。
在项目根目录中使用文件 middleware.ts(或 .js)来定义中间件。例如,与 app 或 pages 同级,或在适用的情况下在 src 内。
- TypeScript
- JavaScript
import { NextResponse, NextRequest } from 'next/server'
// 如果在内部使用 `await`,此函数可以标记为 `async`
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
import { NextResponse } from 'next/server'
// 如果在内部使用 `await`,此函数可以标记为 `async`
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
export const config = {
matcher: '/about/:path*',
}
导出
中间件函数
文件必须导出单个函数,作为默认导出或命名为 middleware。注意,不支持来自同一文件的多个中间件。
// 默认导出示例
export default function middleware(request) {
// 中间件逻辑
}
配置对象 (可选)
可以选择性地与中间件函数一起导出配置对象。此对象包括 matcher 来指定中间件应用的路径。
匹配器
matcher 选项允许你为中间件运行指定特定路径。你可以通过以下几种方式指定这些路径:
- 对于单个路径:直接使用字符串定义路径,如
'/about'。 - 对于多个路径:使用数组列出多个路径,如
matcher: ['/about', '/contact'],这会将中间件应用于/about和/contact。
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
此外,matcher 选项通过正则表达式支持复杂的路径规范,如 matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],实现对包含或排除路径的精确控制。
matcher 选项接受具有以下键的对象数组:
source:用于匹配请求路径的路径或模式。它可以是用于直接路径匹配的字符串,或用于更复杂匹配的模式。regexp(可选):基于源微调匹配的正则表达式字符串。它提供了对包含或排除路径的额外控制。locale(可选):当设置为false时,在路径匹配中忽略基于区域设置的路由。has(可选):基于特定请求元素(如头、查询参数或 cookie)的存在指定条件。missing(可选):专注于某些请求元素缺失的条件,如缺失的头或 cookie。
export const config = {
matcher: [
{
source: '/api/*',
regexp: '^/api/(.*)',
locale: false,
has: [
{ type: 'header', key: 'Authorization', value: 'Bearer Token' },
{ type: 'query', key: 'userId', value: '123' },
],
missing: [{ type: 'cookie', key: 'session', value: 'active' }],
},
],
}
配置的匹配器:
- 必须以
/开头 - 可以包含命名参数:
/about/:path匹配/about/a和/about/b但不匹配/about/a/c - 可以对命名参数有修饰符(以
:开头):/about/:path*匹配/about/a/b/c,因为*是_零个或多个_。?是_零个或一个_,+是_一个或多个_ - 可以使用括号括起来的正则表达式:
/about/(.*)与/about/:path*相同
在 path-to-regexp 文档中了解更多详细信息。
提示:
matcher值需要是常量,以便在构建时进行静态分析。动态值(如变量)将被忽略。- 为了向后兼容,Next.js 始终将
/public视为/public/index。因此,/public/:path的匹配器将匹配。
参数
request
定义中间件时,默认导出函数接受单个参数 request。此参数是 NextRequest 的实例,表示传入的 HTTP 请求。
- TypeScript
- JavaScript
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// 中间件逻辑在这里
}
export function middleware(request) {
// 中间件逻辑在这里
}
提示:
NextRequest是表示 Next.js 中间件中传入 HTTP 请求的类型,而NextResponse是用于操作和发送回 HTTP 响应的类。
NextResponse
NextResponse API 允许你:
redirect将传入请求重定向到不同的 URLrewrite通过显示给定 URL 来重写响应- 为 API 路由、
getServerSideProps和rewrite目标设置请求头 - 设置响应 cookie
- 设置响应头
要从中间件产生响应,你可以:
执行顺序
中间件将为项目中的每个路由调用。考虑到这一点,使用 matchers 精确地定位或排除特定路由至关重要。以下是执行顺序:
next.config.js中的headersnext.config.js中的redirects- 中间件(
rewrites、redirects等) next.config.js中的beforeFiles(rewrites)- 文件系统路由(
public/、_next/static/、pages/、app/等) next.config.js中的afterFiles(rewrites)- 动态路由(
/blog/[slug]) next.config.js中的fallback(rewrites)
运行时
中间件默认使用边缘运行时。从 v15.2 (canary) 开始,我们实验性地支持使用 Node.js 运行时。要启用,请在 next.config 文件中添加标志:
- TypeScript
- JavaScript
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
nodeMiddleware: true,
},
}
export default nextConfig
const nextConfig = {
experimental: {
nodeMiddleware: true,
},
}
export default nextConfig
然后在你的中间件文件中,在 config 对象中将运行时设置为 nodejs:
- JavaScript
- TypeScript
export const config = {
runtime: 'nodejs',
}
export const config = {
runtime: 'nodejs',
}
注意:此功能尚未推荐用于生 产环境。因此,Next.js 将抛出错误,除非你使用 next@canary 版本而不是稳定版本。
高级中间件标志
在 Next.js 的 v13.1 中引入了两个额外的中间件标志,skipMiddlewareUrlNormalize 和 skipTrailingSlashRedirect 来处理高级用例。
skipTrailingSlashRedirect 禁用 Next.js 添加或删除尾随斜杠的重定向。这允许在中间件内进行自定义处理,为某些路径保持尾随斜杠,但不为其他路径保持,这可以使增量迁移更容易。
module.exports = {
skipTrailingSlashRedirect: true,
}
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(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
return NextResponse.redirect(
new URL(`${req.nextUrl.pathname}/`, req.nextUrl)
)
}
}
skipMiddlewareUrlNormalize 允许禁用 Next.js 中的 URL 规范化,使处理直接访问和客户端转换相同。在某些高级情况下,此选项通过使用原始 URL 提供完全控制。
module.exports = {
skipMiddlewareUrlNormalize: true,
}
export default async function middleware(req) {
const { pathname } = req.nextUrl
// GET /_next/data/build-id/hello.json
console.log(pathname)
// 使用标志,这现在是 /_next/data/build-id/hello.json
// 没有标志,这将被规范化为 /hello
}