中间件
中间件允许你在请求完成之前运行代码。 然后,根据传入的请求,你可以通过重写、重定向、修改请求或响应标头或直接响应来修改响应。
英 Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.
中间件在缓存内容和路由匹配之前运行。 详细信息请参见 匹配路径 。
英 Middleware runs before cached content and routes are matched. See Matching Paths for more details.
使用项目根目录中的文件
middleware.ts
(或
.js
)来定义中间件。 例如,与
pages
或
app
处于同一级别,或者在
src
内部(如果适用)。
英
Use the file
middleware.ts
(or
.js
) in the root of your project to define Middleware. For example, at the same level as
pages
or
app
, or inside
src
if applicable.
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request: NextRequest) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
import { NextResponse } from 'next/server'
// This function can be marked `async` if using `await` inside
export function middleware(request) {
return NextResponse.redirect(new URL('/home', request.url))
}
// See "Matching Paths" below to learn more
export const config = {
matcher: '/about/:path*',
}
匹配路径
将为 项目中的每条路线 调用中间件。 以下是执行顺序:
英 Middleware will be invoked for every route in your project . The following is the execution order:
headers
从
next.config.js
redirects
从
next.config.js
rewrites
、
redirects
等)
beforeFiles
(
rewrites
) 从
next.config.js
public/
、
_next/static/
、
pages/
、
app/
等)
afterFiles
(
rewrites
) 从
next.config.js
/blog/[slug]
)
fallback
(
rewrites
) 从
next.config.js
有两种方法可以定义中间件将在哪些路径上运行:
英 There are two ways to define which paths Middleware will run on:
匹配器
matcher
允许你过滤中间件以在特定路径上运行。
英
matcher
allows you to filter Middleware to run on specific paths.
export const config = {
matcher: '/about/:path*',
}
你可以使用数组语法匹配单个路径或多个路径:
英 You can match a single path or multiple paths with an array syntax:
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
matcher
配置允许完整的正则表达式,因此支持负向查找或字符匹配等匹配。 可以在此处查看用于匹配除特定路径之外的所有路径的负前瞻示例:
英
The
matcher
config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here:
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
}
很高兴知道
:
matcher
值需要是常量,以便可以在构建时对其进行静态分析。 诸如变量之类的动态值将被忽略。
配置的匹配器:
英 Configured matchers:
/
开头
/about/:path
匹配
/about/a
和
/about/b
,但不匹配
/about/a/c
:
开头):
/about/:path*
与
/about/a/b/c
匹配,因为
*
为零或更大。
?
为零或一,
+
为一或多个
/about/(.*)
与
/about/:path*
相同
阅读有关 path-to-regexp 文档的更多详细信息。
英 Read more details on path-to-regexp documentation.
很高兴知道
: 为了向后兼容,Next.js 始终将
/public
视为
/public/index
。 因此,
/public/:path
的匹配器将匹配。
条件语句
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
import { NextResponse } from 'next/server'
export function middleware(request) {
if (request.nextUrl.pathname.startsWith('/about')) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
if (request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.rewrite(new URL('/dashboard/user', request.url))
}
}
NextResponse
NextResponse
API 允许你:
英
The
NextResponse
API allows you to:
redirect
传入请求到不同的 URL
rewrite
通过显示给定 URL 进行响应
getServerSideProps
和
rewrite
目标的请求标头
要从中间件生成响应,你可以:
英 To produce a response from Middleware, you can:
rewrite
到产生响应的路由(
页面
或
路由处理程序
)
NextResponse
。 见
产生响应
使用 Cookie
Cookie 是常规标头。 在
Request
上,它们存储在
Cookie
标头中。 在
Response
上,它们位于
Set-Cookie
标头中。 Next.js 通过
NextRequest
和
NextResponse
上的
cookies
扩展提供了一种便捷的方式来访问和操作这些 cookie。
英
Cookies are regular headers. On a
Request
, they are stored in the
Cookie
header. On a
Response
they are in the
Set-Cookie
header. Next.js provides a convenient way to access and manipulate these cookies through the
cookies
extension on
NextRequest
and
NextResponse
.
cookies
带有以下方法:
get
、
getAll
、
set
和
delete
cookie。 你可以检查
has
的 cookie 是否存在,或者删除
clear
的所有 cookie。
cookies
有以下方法
get
、
getAll
、
set
和
delete
。
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
return response
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// Assume a "Cookie:nextjs=fast" header to be present on the incoming request
// Getting cookies from the request using the `RequestCookies` API
let cookie = request.cookies.get('nextjs')
console.log(cookie) // => { name: 'nextjs', value: 'fast', Path: '/' }
const allCookies = request.cookies.getAll()
console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }]
request.cookies.has('nextjs') // => true
request.cookies.delete('nextjs')
request.cookies.has('nextjs') // => false
// Setting cookies on the response using the `ResponseCookies` API
const response = NextResponse.next()
response.cookies.set('vercel', 'fast')
response.cookies.set({
name: 'vercel',
value: 'fast',
path: '/',
})
cookie = response.cookies.get('vercel')
console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' }
// The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header.
return response
}
设置标题
你可以使用
NextResponse
API 设置请求和响应标头(自 Next.js v13.0.0 起可以设置请求标头)。
英
You can set request and response headers using the
NextResponse
API (setting
request
headers is available since Next.js v13.0.0).
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
import { NextResponse } from 'next/server'
export function middleware(request) {
// Clone the request headers and set a new header `x-hello-from-middleware1`
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-hello-from-middleware1', 'hello')
// You can also set request headers in NextResponse.rewrite
const response = NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
// Set a new response header `x-hello-from-middleware2`
response.headers.set('x-hello-from-middleware2', 'hello')
return response
}
很高兴知道 : 避免设置大标头,因为它可能会导致 431 请求标头字段太大 错误,具体取决于你的后端 Web 服务器配置。
产生响应
你可以通过返回
Response
或
NextResponse
实例直接从中间件进行响应。 (从
Next.js v13.1.0
年开始可用)
英
You can respond from Middleware directly by returning a
Response
or
NextResponse
instance. (This is available since
Next.js v13.1.0
)
import { NextRequest } from 'next/server'
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request: NextRequest) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
import { isAuthenticated } from '@lib/auth'
// Limit the middleware to paths starting with `/api/`
export const config = {
matcher: '/api/:function*',
}
export function middleware(request) {
// Call our authentication function to check the request
if (!isAuthenticated(request)) {
// Respond with JSON indicating an error message
return Response.json(
{ success: false, message: 'authentication failed' },
{ status: 401 }
)
}
}
高级中间件标志
在 Next.js 的
v13.1
中,为中间件引入了两个附加标志
skipMiddlewareUrlNormalize
和
skipTrailingSlashRedirect
来处理高级用例。
英
In
v13.1
of Next.js two additional flags were introduced for middleware,
skipMiddlewareUrlNormalize
and
skipTrailingSlashRedirect
to handle advanced use cases.
skipTrailingSlashRedirect
允许禁用 Next.js 默认重定向以添加或删除尾部斜杠,从而允许中间件内部进行自定义处理,这可以允许维护某些路径的尾部斜杠,但不允许维护其他路径的尾部斜杠,从而允许更轻松的增量迁移。
英
skipTrailingSlashRedirect
allows disabling Next.js default redirects for adding or removing trailing slashes allowing custom handling inside middleware which can allow maintaining the trailing slash for some paths but not others allowing easier incremental migrations.
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()
}
// apply trailing slash handling
if (
!pathname.endsWith('/') &&
!pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/)
) {
req.nextUrl.pathname += '/'
return NextResponse.redirect(req.nextUrl)
}
}
skipMiddlewareUrlNormalize
允许禁用 Next.js 的 URL 规范化功能,以使直接访问和客户端转换的处理相同。 在某些高级情况下,你需要使用解锁的原始 URL 进行完全控制。
英
skipMiddlewareUrlNormalize
allows disabling the URL normalizing Next.js does to make handling direct visits and client-transitions the same. There are some advanced cases where you need full control using the original URL which this unlocks.