Next.js 13 App目录动态路由匹配无效时返回404的方案咨询
解决Next.js 13.4.4 App目录+next-intl下动态路由404不生效问题
针对你遇到的问题,以下是几种比组件内单独调用notFound()更优的实现方式:
1. 确保generateStaticParams生成完整多语言路径组合(推荐)
App目录下dynamicParams: false生效的核心前提是,generateStaticParams必须生成所有包含多语言前缀的有效路由参数组合——因为next-intl的路由结构是[locale]/post/[slug],参数是locale和slug的绑定组合,少了任何一个都会导致Next.js无法正确识别无效路径。
示例代码(app/[locale]/post/[slug]/page.tsx):
import { notFound } from 'next/navigation'; import { getPosts, getPostBySlugAndLocale } from '@/lib/posts'; import { locales } from '@/i18n.config'; // 禁用动态参数,未预生成的路径直接返回404 export const dynamicParams = false; // 生成所有有效locale+slug的参数组合 export async function generateStaticParams() { const posts = await getPosts(); return locales.flatMap(locale => posts.map(post => ({ locale, slug: post.slug })) ); } async function Page({ params }: { params: { locale: string; slug: string } }) { // 兜底校验(防止预生成时的遗漏) const post = await getPostBySlugAndLocale(params.slug, params.locale); if (!post) notFound(); return ( <article> <h1>{post.title}</h1> <div>{post.content}</div> </article> ); } export default Page;
如果你的generateStaticParams已经正确生成完整组合但仍不生效,可能是next-intl的路由配置干扰了Next.js的静态路径匹配,可以结合下面的Middleware方案。
2. 用Middleware提前拦截无效路由
通过Next.js的Middleware在请求到达页面组件前就校验路径有效性,直接返回404,避免组件层面的冗余逻辑,性能更优。
示例代码(middleware.ts):
import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { locales } from './i18n.config'; import { checkPostExists } from '@/lib/posts'; export async function middleware(request: NextRequest) { const pathname = request.nextUrl.pathname; // 匹配多语言文章路由(如/en/post/foo、/zh/post/bar) const routeMatch = pathname.match(/^\/([a-z]{2})\/post\/([^\/]+)$/); if (routeMatch) { const [, locale, slug] = routeMatch; // 校验语言是否合法 if (!locales.includes(locale)) { return new NextResponse('Not Found', { status: 404 }); } // 校验文章是否存在 const postExists = await checkPostExists(slug, locale); if (!postExists) { return new NextResponse('Not Found', { status: 404 }); } } return NextResponse.next(); } // 指定Middleware生效的路径范围 export const config = { matcher: ['/([a-z]{2})/post/:path*'], };
3. 优化组件内的notFound校验逻辑
如果坚持用组件内校验,可以把校验逻辑抽成可复用的Server Component,让页面代码更简洁:
// app/[locale]/post/[slug]/PostNotFoundGuard.tsx 'use server'; import { notFound } from 'next/navigation'; import { getPostBySlugAndLocale } from '@/lib/posts'; export async function PostNotFoundGuard({ locale, slug }: { locale: string; slug: string }) { const post = await getPostBySlugAndLocale(slug, locale); if (!post) notFound(); return null; }
在页面组件中引入:
import { PostNotFoundGuard } from './PostNotFoundGuard'; import { getPostBySlugAndLocale } from '@/lib/posts'; async function Page({ params }: { params: { locale: string; slug: string } }) { // 先执行校验,无效则直接返回404 await PostNotFoundGuard(params); const post = await getPostBySlugAndLocale(params.slug, params.locale); return <article>{/* 渲染文章内容 */}</article>; } export const dynamicParams = false; export async function generateStaticParams() { /* 同上 */ } export default Page;
内容的提问来源于stack exchange,提问作者Bambou
相关产品推荐
相关产品推荐

