如何在Next.js中间件中使用异步自定义Hook?
问题分析与解决方案
错误原因
- React Hook规则违反:自定义Hook(以
use开头)只能在React函数组件或其他自定义Hook内部调用,不能直接在Next.js中间件这种普通服务器端函数中使用。 - 环境不兼容:中间件运行在边缘/服务器端环境,不存在浏览器的
document对象,你的代码中引入的依赖(如emotion)尝试访问document导致报错。 - 相对路径无效:中间件中使用相对路径
/redirect/list发起fetch请求会失败,因为服务器端没有浏览器的页面上下文,必须使用完整URL。
修正方案
将原自定义Hook重构为普通异步工具函数,脱离React Hook体系,适配中间件的运行环境:
1. 重构重定向检查逻辑为普通函数
创建redirectUtils.js文件:
export const getRedirectChecker = async () => { // 使用完整URL发起请求,可通过环境变量或request上下文获取域名 const baseUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000' const response = await fetch(`${baseUrl}/redirect/list`) const redirects = await response.json() const checkRedirection = (url) => { let isRedirected = false let newUrl = url.href // 补全你的重定向匹配逻辑,示例如下: const matchedRule = redirects.find(rule => url.pathname === rule.from) if (matchedRule) { isRedirected = true newUrl = `${url.origin}${matchedRule.to}` } return { isRedirected, newUrl } } return checkRedirection }
2. 修改中间件代码
更新middleware.js:
import { NextResponse } from 'next/server' import { getRedirectChecker } from './redirectUtils' export async function middleware(request) { // 跳过Next.js内部资源请求 if (request.nextUrl.pathname.startsWith('/_next')) { return NextResponse.next() } const checkRedirection = await getRedirectChecker() const { isRedirected, newUrl } = checkRedirection(request.nextUrl) if (isRedirected) { // 返回标准的重定向响应 return NextResponse.redirect(newUrl) } // 无重定向时继续请求流程 return NextResponse.next() }
关键说明
- 去掉
use前缀,将逻辑转为纯服务器端可执行的工具函数,避免Hook规则限制。 - 确保fetch请求使用完整URL,可通过环境变量
NEXT_PUBLIC_APP_URL配置生产环境域名,本地开发默认使用http://localhost:3000。 - 中间件必须返回
NextResponse对象,不能直接返回字符串URL,否则会导致响应格式错误。
内容的提问来源于stack exchange,提问作者Ali EXE
相关产品推荐
相关产品推荐

