编辑:更新了Next 12.2+的答案。
注意:以下内容摘自官方博客文章,因为SO通常不鼓励链接会随着时间变得过时/失效。https://nextjs.org/blog/next-12-2#middleware-stable
中间件现在稳定在12.2版本,并且基于用户反馈,具有改进的API。
import { NextRequest, NextResponse } from 'next/server';
export function middleware(req: NextRequest) {
const isInBeta = JSON.parse(req.cookies.get('beta') || 'false');
req.nextUrl.pathname = isInBeta ? '/beta' : '/';
return NextResponse.rewrite(req.nextUrl);
}
export const config = {
matcher: '/',
};
迁移指南
https://nextjs.org/docs/messages/middleware-upgrade-guide
重大变更
- 不支持嵌套中间件
- 无响应体
- Cookie API已升级
- 新增User-Agent助手
- 不再有页面匹配数据
- 在内部Next.js请求上执行中间件
如何升级
您应该在应用程序中声明一个单一的中间件文件,该文件应该位于pages
目录旁边,并且没有_
前缀。您的中间件文件仍然可以具有.ts
或.js
扩展名。
中间件将为应用程序中的每个路由调用,并且可以使用自定义匹配器来定义匹配过滤器。以下是一个中间件的示例,触发/about/*
和/dashboard/:path*
路由的自定义匹配器定义在导出的配置对象中:
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))
}
export const config = {
matcher: ['/about/:path*', '/dashboard/:path*'],
}
编辑: 适用于Next > 12和 < 12.2的过时答案
随着Next.js 12的发布,现在有beta版支持使用Vercel Edge Functions的中间件。
https://nextjs.org/blog/next-12#introducing-middleware
中间件使用严格的运行时环境,支持标准Web API,如fetch。使用next start即可立即实现,也可以在使用Edge函数的平台(如Vercel)上进行。
要在Next.js中使用Middleware,您可以创建文件pages/_middleware.js。在本示例中,我们使用了标准Web API响应(MDN):
export function middleware(req, ev) {
return new Response('Hello, world!')
}
JWT身份验证示例
在next.config.js
中:
const withTM = require('@vercel/edge-functions-ui/transpile')()
module.exports = withTM()
在 pages/_middleware.js
中:
import { NextRequest, NextResponse } from 'next/server'
import { setUserCookie } from '@lib/auth'
export function middleware(req: NextRequest) {
return setUserCookie(req, NextResponse.next())
}
在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 })
}
}
在
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.' } })
}
}