NextJS 13重定向后页面空白问题求助(API路由+中间件场景)
问题分析
你的核心问题出在两个关键环节:
- Middleware路由范围未做限制:当前Middleware会拦截所有请求(包括
/login页面和api路由本身),导致/login页面的请求被强制转发到api/authUser,而该接口检测到请求路径是/login时直接返回空响应,最终页面显示空白。 - 错误使用
rewrite而非直接处理校验/跳转:NextResponse.rewrite是服务器内部请求转发,会把目标路由的响应作为当前请求的返回内容。你需要的是未登录时的页面跳转,应该用redirect,或者直接在Middleware中完成校验逻辑,而非转发到API路由。
解决方案
方案1:在Middleware中直接完成登录校验(推荐)
Next.js 13的Middleware支持在边缘函数中直接调用外部接口,无需转发到内部API路由,逻辑更高效清晰。
修改src/middleware.ts:
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export async function middleware(req: NextRequest) { // 定义需要校验的受保护路由 const currentPath = req.nextUrl.pathname // 排除无需校验的路由:登录页、API路由、静态资源 if (currentPath === '/login' || currentPath.startsWith('/api/') || currentPath.startsWith('/_next/')) { return NextResponse.next() } try { // 调用后端校验接口,传递原请求Cookie以识别用户状态 const res = await fetch('http://localhost:8082/api/auth/user', { method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json', Cookie: req.headers.get('Cookie') || '' }, credentials: 'include' }) const data = await res.json() // 校验失败则重定向到登录页 if (data.error) { return NextResponse.redirect(new URL('/login', req.url)) } // 校验通过,正常放行 return NextResponse.next() } catch (err) { // 接口调用异常时也重定向到登录页 console.error('登录校验失败:', err) return NextResponse.redirect(new URL('/login', req.url)) } } // 可选:指定Middleware生效的路由范围,缩小拦截范围 export const config = { matcher: ['/', '/dashboard/:path*'] // 替换为你的受保护路由 }
方案2:保留API路由但修复Middleware逻辑
如果坚持通过内部API路由处理校验,需调整Middleware避免拦截关键路由:
修改src/middleware.ts:
import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(req: NextRequest) { const currentPath = req.nextUrl.pathname // 排除登录页、API路由、静态资源,避免循环拦截 if (currentPath === '/login' || currentPath.startsWith('/api/') || currentPath.startsWith('/_next/')) { return NextResponse.next() } // 仅对受保护页面转发到校验接口 return NextResponse.rewrite(new URL('/api/authUser', req.url)) }
同时修改api/authUser.ts,确保校验逻辑正确:
import type { NextApiRequest, NextApiResponse } from 'next' type Data = { error?: string user?: any } export default async function handler( req: NextApiRequest, res: NextApiResponse<Data> ) { try { const options = { method: 'GET', headers: { 'Content-Type': 'application/json', Accept: 'application/json', Cookie: req.headers.cookie || '' }, credentials: 'include' } const response = await fetch('http://localhost:8082/api/auth/user', options) const data = await response.json() if (data.error) { res.writeHead(302, { Location: '/login' }) return res.end() } // 校验通过返回用户信息,或直接放行(此方案更适合方案1) res.status(200).json({ user: data }) } catch (err) { res.writeHead(302, { Location: '/login' }) res.end() } } export const config = { api: { externalResolver: true, }, }
关键注意点
- 路由排除:必须排除
/login、API路由和/_next/静态资源路径,否则会出现循环拦截或静态资源加载失败的问题。 - Cookie传递:调用后端校验接口时要传递原请求的Cookie,否则后端无法识别用户登录状态。
rewritevsredirect:rewrite是服务器内部转发,地址栏不变化;redirect是浏览器跳转,地址栏更新。登录校验场景下优先用redirect实现页面跳转。
内容的提问来源于stack exchange,提问作者pragmatic84
相关产品推荐
相关产品推荐

