遇到 NEXT_REDIRECT 或 notFound() 错误如何排查?
NEXT_REDIRECT 错误排查
1. 错误类型和原因
// 常见的 NEXT_REDIRECT 错误
// 1. 在客户端组件中使用 redirect()
'use client'
import { redirect } from 'next/navigation'
export default function ClientComponent() {
// ❌ 错误:不能在客户端组件中使用 redirect()
const handleClick = () => {
redirect('/dashboard') // 会抛出 NEXT_REDIRECT 错误
}
return <button onClick={handleClick}>Go to Dashboard</button>
}
// 2. 在异步函数中错误使用
export default async function ServerComponent() {
// ❌ 错误:在异步函数中直接使用 redirect()
const data = await fetchData()
if (!data) {
redirect('/404') // 可能抛出 NEXT_REDIRECT 错误
}
return <div>{data}</div>
}
2. 正确的重定向方式
// ✅ 正确:在服务器组件中使用 redirect()
// app/login/page.js
import { redirect } from 'next/navigation'
import { cookies } from 'next/headers'
export default async function LoginPage() {
const cookieStore = cookies()
const token = cookieStore.get('auth-token')
// 如果已登录,重定向到仪表盘
if (token) {
redirect('/dashboard')
}
return (
<div>
<h1>Login</h1>
{/* 登录表单 */}
</div>
)
}
// ✅ 正确:在客户端组件中使用 useRouter
'use client'
import { useRouter } from 'next/navigation'
export default function ClientComponent() {
const router = useRouter()
const handleClick = () => {
router.push('/dashboard') // 正确的客户端重定向
}
return <button onClick={handleClick}>Go to Dashboard</button>
}
3. 错误处理
// app/api/protected/route.js
import { NextResponse } from 'next/server'
import { redirect } from 'next/navigation'
export async function GET(request) {
try {
const token = request.headers.get('authorization')
if (!token) {
// ✅ 正确:在 API 路由中使用 NextResponse.redirect
return NextResponse.redirect(new URL('/login', request.url))
}
// 处理请求
return NextResponse.json({ message: 'Success' })
} catch (error) {
console.error('API Error:', error)
return NextResponse.json(
{ error: 'Internal server error' },
{ status: 500 }
)
}
}
notFound() 错误排查
1. 错误类型和原因
// 常见的 notFound() 错误
// 1. 在客户端组件中使用 notFound()
'use client'
import { notFound } from 'next/navigation'
export default function ClientComponent() {
// ❌ 错误:不能在客户端组件中使用 notFound()
const handleError = () => {
notFound() // 会抛出错误
}
return <button onClick={handleError}>Trigger 404</button>
}
// 2. 在异步函数中错误使用
export default async function ServerComponent() {
// ❌ 错误:在异步函数中直接使用 notFound()
const data = await fetchData()
if (!data) {
notFound() // 可能抛出错误
}
return <div>{data}</div>
}
2. 正确的 404 处理方式
// ✅ 正确:在服务器组件中使用 notFound()
// app/blog/[slug]/page.js
import { notFound } from 'next/navigation'
export default async function BlogPost({ params }) {
const { slug } = params
try {
const post = await fetch(`https://api.example.com/posts/${slug}`)
if (!post.ok) {
notFound() // 正确的 404 处理
}
const data = await post.json()
return (
<article>
<h1>{data.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.content }} />
</article>
)
} catch (error) {
console.error('Error fetching post:', error)
notFound()
}
}
// ✅ 正确:在客户端组件中使用条件渲染
'use client'
import { useState } from 'react'
export default function ClientComponent() {
const [data, setData] = useState(null)
const [error, setError] = useState(null)
const fetchData = async () => {
try {
const response = await fetch('/api/data')
if (!response.ok) {
setError('Data not found')
return
}
const result = await response.json()
setData(result)
} catch (err) {
setError('Failed to fetch data')
}
}
if (error) {
return <div>Error: {error}</div>
}
if (!data) {
return <div>Loading...</div>
}
return <div>{data}</div>
}
3. 自定义 404 页面
// app/not-found.js
export default function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="/">Go back home</a>
</div>
)
}
调试技巧
1. 错误日志
// 添加详细的错误日志
export default async function ServerComponent() {
try {
const data = await fetchData()
if (!data) {
console.error('Data not found for:', params)
notFound()
}
return <div>{data}</div>
} catch (error) {
console.error('Error in ServerComponent:', error)
throw error
}
}
2. 错误边界
// app/error.js
'use client'
import { useEffect } from 'react'
export default function Error({ error, reset }) {
useEffect(() => {
console.error('Error:', error)
}, [error])
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
)
}
3. 开发环境调试
// 开发环境下的调试信息
export default async function ServerComponent() {
if (process.env.NODE_ENV === 'development') {
console.log('ServerComponent rendered with params:', params)
}
const data = await fetchData()
if (!data) {
if (process.env.NODE_ENV === 'development') {
console.error('Data not found, redirecting to 404')
}
notFound()
}
return <div>{data}</div>
}
常见问题和解决方案
1. 重定向循环
// ❌ 错误:可能导致重定向循环
export default async function Page() {
const user = await getCurrentUser()
if (!user) {
redirect('/login')
}
if (user.role !== 'admin') {
redirect('/unauthorized')
}
return <div>Admin content</div>
}
// ✅ 正确:避免重定向循环
export default async function Page() {
const user = await getCurrentUser()
if (!user) {
redirect('/login')
}
if (user.role !== 'admin') {
redirect('/unauthorized')
}
return <div>Admin content</div>
}
2. 异步操作中的错误处理
// ❌ 错误:在异步操作中直接使用 redirect/notFound
export default async function Page() {
const data = await fetchData()
if (!data) {
notFound() // 可能不会正确工作
}
return <div>{data}</div>
}
// ✅ 正确:使用 try-catch 处理异步错误
export default async function Page() {
try {
const data = await fetchData()
if (!data) {
notFound()
}
return <div>{data}</div>
} catch (error) {
console.error('Error fetching data:', error)
notFound()
}
}
3. 客户端重定向
// ✅ 正确:客户端重定向
'use client'
import { useRouter } from 'next/navigation'
import { useEffect } from 'react'
export default function ClientComponent() {
const router = useRouter()
useEffect(() => {
const checkAuth = async () => {
const token = localStorage.getItem('auth-token')
if (!token) {
router.push('/login')
}
}
checkAuth()
}, [router])
return <div>Protected content</div>
}
最佳实践
1. 错误处理策略
// 统一的错误处理
export async function handleServerError(error, context) {
console.error(`Error in ${context}:`, error)
if (error.status === 404) {
notFound()
}
if (error.status === 401) {
redirect('/login')
}
throw error
}
2. 类型安全
// 类型安全的错误处理
interface ApiResponse<T> {
data: T | null
error: string | null
}
export async function fetchData<T>(url: string): Promise<T> {
try {
const response = await fetch(url)
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const result: ApiResponse<T> = await response.json()
if (result.error) {
throw new Error(result.error)
}
if (!result.data) {
notFound()
}
return result.data
} catch (error) {
console.error('Error fetching data:', error)
throw error
}
}
总结
错误排查要点:
NEXT_REDIRECT 错误:
- 不能在客户端组件中使用 redirect()
- 在 API 路由中使用 NextResponse.redirect
- 在客户端使用 useRouter
notFound() 错误:
- 不能在客户端组件中使用 notFound()
- 在服务器组件中正确使用
- 创建自定义 404 页面
调试技巧:
- 添加错误日志
- 使用错误边界
- 开发环境调试
最佳实践:
- 避免重定向循环
- 正确处理异步错误
- 统一的错误处理策略