Edit:Next 12.2+ 的更新答案
注意:以下内容是从官方博客文章复制的,因为 SO 通常不鼓励随着时间的推移而变得陈旧/失效的链接https://nextjs.org/blog/next-12-2#middleware-stable https://nextjs.org/blog/next-12-2#middleware-stable
中间件现已稳定在 12.2 中,并根据用户反馈改进了 API。
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
// If the incoming request has the "beta" cookie
// then we'll rewrite the request to /beta
export function middleware(req: NextRequest) {
const isInBeta = JSON.parse(req.cookies.get('beta') || 'false');
req.nextUrl.pathname = isInBeta ? '/beta' : '/';
return NextResponse.rewrite(req.nextUrl);
}
// Supports both a single value or an array of matches
export const config = {
matcher: '/',
};
迁移指南
https://nextjs.org/docs/messages/middleware-upgrade-guide https://nextjs.org/docs/messages/middleware-upgrade-guide重大变化
- 无嵌套中间件
- 无响应体
- Cookie API 已改进
- 新的用户代理助手
- 不再有页面匹配数据
- 对内部 Next.js 请求执行中间件
如何升级
你应该声明一个中间件文件在您的应用程序中,它应该位于pages
目录和命名without an _
字首。您的中间件文件仍然可以有.ts
or .js
扩大。
中间件将被调用应用程序中的每条路线,并且可以使用自定义匹配器来定义匹配过滤器。以下是触发的中间件示例/about/*
and /dashboard/:path*
,自定义匹配器在导出的配置对象中定义:
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL('/about-2', request.url))
}
// Supports both a single string value or an array of matchers
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
Edit:下一个 > 12 和
随着 Next.js 12 的发布,现在对使用 Vercel Edge Functions 的中间件提供了测试版支持。
https://nextjs.org/blog/next-12#introducing-middleware https://nextjs.org/blog/next-12#introducing-middleware
中间件使用严格的运行时,支持 fetch 等标准 Web API。 > 使用 next start 即可开箱即用,也可在 Vercel 等使用 Edge Functions 的 Edge 平台上使用。
要在Next.js中使用中间件,您可以创建文件pages/_middleware.js。在此示例中,我们使用标准 Web API 响应 (MDN):
// pages/_middleware.js
export function middleware(req, ev) {
return new Response('Hello, world!')
}
JWT 身份验证示例
- https://github.com/vercel/examples/tree/main/edge-functions/jwt-authentication https://github.com/vercel/examples/tree/main/edge-functions/jwt-authentication
in next.config.js
:
const withTM = require('@vercel/edge-functions-ui/transpile')()
module.exports = withTM()
in pages/_middleware.js
:
import { NextRequest, NextResponse } from 'next/server'
import { setUserCookie } from '@lib/auth'
export function middleware(req: NextRequest) {
// Add the user token to the response
return setUserCookie(req, NextResponse.next())
}
in pages/api/_middleware.js
:
import type { NextRequest } from 'next/server'
import { nanoid } from 'nanoid'
import { verifyAuth } from '@lib/auth'
import { jsonResponse } from '@lib/utils'
export async function middleware(req: NextRequest) {
const url = req.nextUrl
if (url.searchParams.has('edge')) {
const resOrPayload = await verifyAuth(req)
return resOrPayload instanceof Response
? resOrPayload
: jsonResponse(200, { nanoid: nanoid(), jwtID: resOrPayload.jti })
}
}
in pages/api/index.js
:
import type { NextApiRequest, NextApiResponse } from 'next'
import { verify, JwtPayload } from 'jsonwebtoken'
import { nanoid } from 'nanoid'
import { USER_TOKEN, JWT_SECRET_KEY } from '@lib/constants'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({
error: { message: 'Method not allowed' },
})
}
try {
const token = req.cookies[USER_TOKEN]
const payload = verify(token, JWT_SECRET_KEY) as JwtPayload
res.status(200).json({ nanoid: nanoid(), jwtID: payload.jti })
} catch (err) {
res.status(401).json({ error: { message: 'Your token has expired.' } })
}
}