Next.js 13.4.7中如何正确链式调用多个中间件?
在Next.js 13.4.7中正确链式调用多个中间件
你的核心问题是没有处理next-intl中间件的返回值——Next.js中间件的逻辑依赖返回的NextResponse对象生效,直接调用i18nMiddleware(request)但不返回,会导致国际化逻辑完全不生效。下面提供两种可靠的链式调用方案:
方案一:顺序执行(适合少量中间件场景)
先执行国际化中间件,确认它没有触发重定向后,再执行自定义的Token验证逻辑,最后返回国际化中间件的响应以确保其生效。
修改后的完整代码:
import { NextRequest, NextResponse } from 'next/server' import jwt from 'jsonwebtoken' import createMiddleware from 'next-intl/middleware' const locales = ['en', 'fr'] const publicPages = ['/', '/signin', '/register'] const verifPages = ['/verifyaccount', '/resetpassword'] const i18nMiddleware = createMiddleware({ locales, defaultLocale: 'en', }) export const middleware = (request: NextRequest) => { // 1. 先执行国际化中间件,获取响应结果 const i18nResponse = i18nMiddleware(request) // 如果国际化中间件已触发重定向(比如自动补全locale),直接返回该响应 if (i18nResponse.status !== 200) { return i18nResponse } // 2. 执行自定义Token验证逻辑 const path = request.nextUrl.pathname const publicPathnameRegex = RegExp( `^(/(${locales.join('|')}))?(${publicPages.join('|')})?/?$`, 'i' ) const verifPathnameRegex = RegExp( `^(/(${locales.join('|')}))?(${verifPages.join('|')})?/?$`, 'i' ) const isPublicPage = publicPathnameRegex.test(path) const isVerifPage = verifPathnameRegex.test(path) const token = request.cookies.get('token')?.value || '' const decodedToken: any = jwt.decode(token) const headQuarter = decodedToken?.headQuarter // 基于当前locale生成重定向地址(替代原硬编码的fr) const redirectUrl = `/${request.nextUrl.locale}/hxoo/${headQuarter}/dashboard` if (isPublicPage && token && !isVerifPage) { return NextResponse.redirect(new URL(redirectUrl, request.nextUrl)) } if (!isPublicPage && !token && !isVerifPage) { // 重定向到带当前locale的登录页 return NextResponse.redirect(new URL(`/${request.nextUrl.locale}/signin`, request.nextUrl)) } if (!isPublicPage && token) { const routeAccess = decodedToken?.routeAccess const pathParts = path.split('/') const route = `/${pathParts[3]}` if (!routeAccess.includes(route)) { return NextResponse.redirect(new URL(redirectUrl, request.nextUrl)) } } // 3. 返回国际化中间件的响应,确保其逻辑生效 return i18nResponse } export const config = { matcher: [ '/((?!api|_next|.*\\..*).*)', ], }
方案二:中间件组合函数(适合多个中间件场景)
如果后续需要添加更多中间件,可以封装一个组合函数,按顺序执行所有中间件,一旦某个中间件触发重定向,就终止后续逻辑。
import { NextRequest, NextResponse } from 'next/server' import jwt from 'jsonwebtoken' import createMiddleware from 'next-intl/middleware' const locales = ['en', 'fr'] const publicPages = ['/', '/signin', '/register'] const verifPages = ['/verifyaccount', '/resetpassword'] // 定义中间件类型 type Middleware = (request: NextRequest) => NextResponse // 组合多个中间件的工具函数 const chainMiddlewares = (...middlewares: Middleware[]) => { return (request: NextRequest) => { for (const middleware of middlewares) { const response = middleware(request) // 若中间件返回非200响应(重定向/错误),直接返回并终止链 if (response.status !== 200) { return response } } return NextResponse.next() } } // 1. 国际化中间件 const i18nMiddleware = createMiddleware({ locales, defaultLocale: 'en', }) // 2. 自定义Token验证中间件 const authMiddleware: Middleware = (request: NextRequest) => { const path = request.nextUrl.pathname const publicPathnameRegex = RegExp( `^(/(${locales.join('|')}))?(${publicPages.join('|')})?/?$`, 'i' ) const verifPathnameRegex = RegExp( `^(/(${locales.join('|')}))?(${verifPages.join('|')})?/?$`, 'i' ) const isPublicPage = publicPathnameRegex.test(path) const isVerifPage = verifPathnameRegex.test(path) const token = request.cookies.get('token')?.value || '' const decodedToken: any = jwt.decode(token) const headQuarter = decodedToken?.headQuarter const redirectUrl = `/${request.nextUrl.locale}/hxoo/${headQuarter}/dashboard` if (isPublicPage && token && !isVerifPage) { return NextResponse.redirect(new URL(redirectUrl, request.nextUrl)) } if (!isPublicPage && !token && !isVerifPage) { return NextResponse.redirect(new URL(`/${request.nextUrl.locale}/signin`, request.nextUrl)) } if (!isPublicPage && token) { const routeAccess = decodedToken?.routeAccess const pathParts = path.split('/') const route = `/${pathParts[3]}` if (!routeAccess.includes(route)) { return NextResponse.redirect(new URL(redirectUrl, request.nextUrl)) } } return NextResponse.next() } // 链式组合中间件(执行顺序:先i18n,再auth) export const middleware = chainMiddlewares(i18nMiddleware, authMiddleware) export const config = { matcher: [ '/((?!api|_next|.*\\..*).*)', ], }
关键注意点
- 必须返回中间件的响应:第三方中间件(如
next-intl)的逻辑依赖返回的NextResponse才能生效,不能只调用不返回。 - 重定向地址要基于当前Locale:利用
request.nextUrl.locale获取国际化处理后的语言标识,避免硬编码导致路径错误。 - 终止链的条件:如果某个中间件触发了重定向(状态码非200),后续中间件不需要再执行,直接返回该响应即可。
内容的提问来源于stack exchange,提问作者Laroussi Nader
相关产品推荐
相关产品推荐

