You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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|.*\\..*).*)',
  ],
}

关键注意点

  1. 必须返回中间件的响应:第三方中间件(如next-intl)的逻辑依赖返回的NextResponse才能生效,不能只调用不返回。
  2. 重定向地址要基于当前Locale:利用request.nextUrl.locale获取国际化处理后的语言标识,避免硬编码导致路径错误。
  3. 终止链的条件:如果某个中间件触发了重定向(状态码非200),后续中间件不需要再执行,直接返回该响应即可。

内容的提问来源于stack exchange,提问作者Laroussi Nader

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 01:12:26