Next.js App Router中食谱详情页404问题求助
解决Next.js App Router食谱详情页404及数据获取错误问题
问题根源
- 动态路由目录结构错误:你当前的目录是
app > recipes > recipecard,但App Router的动态路由需要创建app/recipes/[slug]/page.tsx的结构,否则访问/recipes/salted-egg-yolks时找不到对应页面,直接触发404。 - App Router不兼容
getServerSideProps:你在RecipeDetails中使用的getServerSideProps是Pages Router的API,App Router中已不再支持,需要改用Server Component异步获取数据。 - 参数逻辑错误:你错误地将URL中的
slug(食谱名称)当成了Edamam的uri来解码,实际uri是通过query参数传递的,应该从URL的查询参数中提取。
修复步骤
1. 调整目录结构
创建正确的动态路由页面目录:
app/ └── recipes/ └── [slug]/ └── page.tsx # 将原RecipeDetails组件迁移到这里
2. 修改RecipeCard组件的链接(可选优化)
保持原有逻辑的同时,推荐使用Next.js的结构化路由写法,避免字符串拼接可能的错误:
import React from 'react'; import Link from 'next/link'; import { slugify } from '@/app/utils/slugify'; interface RecipeCardProps { recipe: { uri: string; label: string; image: string; ingredientLines: string[]; }; } const RecipeCard: React.FC<RecipeCardProps> = ({ recipe }) => { const slug = slugify(recipe.label); return ( <div className="recipe-card"> <h3>{recipe.label}</h3> <img src={recipe.image} alt={recipe.label} /> <p>{recipe.ingredientLines.slice(0, 3).join(', ')}...</p> {/* 使用结构化路由写法 */} <Link href={{ pathname: '/recipes/[slug]', query: { slug: slug, uri: encodeURIComponent(recipe.uri) } }} > View Details </Link> </div> ); }; export default RecipeCard;
3. 重构RecipeDetails为App Router兼容的Server Component
将原RecipeDetails组件迁移到app/recipes/[slug]/page.tsx,改为Server Component并修正参数获取逻辑:
import axios from 'axios'; const APP_ID = process.env.NEXT_PUBLIC_APP_ID; const APP_KEY = process.env.NEXT_PUBLIC_APP_KEY; interface Recipe { uri: string; label: string; image: string; ingredientLines: string[]; calories: number; totalWeight: number; cuisineType: string[]; mealType: string[]; dishType: string[]; } interface PageProps { params: { slug: string }; searchParams: { uri: string }; } export default async function RecipeDetails({ params, searchParams }: PageProps) { // 从查询参数中获取解码后的uri const decodedUri = decodeURIComponent(searchParams.uri); let recipe: Recipe | null = null; try { const API_URL = `https://api.edamam.com/api/recipes/v2/by-uri?type=public&app_id=${APP_ID}&app_key=${APP_KEY}&uri=${encodeURIComponent(decodedUri)}`; const response = await axios.get(API_URL); recipe = response.data.recipe || null; } catch (error) { console.error('获取食谱详情失败:', error); } if (!recipe) { return <div>未找到该食谱</div>; } return ( <div> <h1>{recipe.label}</h1> <img src={recipe.image} alt={recipe.label} /> <ul> {recipe.ingredientLines.map((ingredient, index) => ( <li key={index}>{ingredient}</li> ))} </ul> <p><strong>热量:</strong> {recipe.calories.toFixed(2)}</p> <p><strong>总重量:</strong> {recipe.totalWeight.toFixed(2)}g</p> <p><strong>菜系:</strong> {recipe.cuisineType.join(', ')}</p> <p><strong>餐型:</strong> {recipe.mealType.join(', ')}</p> <p><strong>菜品类型:</strong> {recipe.dishType.join(', ')}</p> </div> ); }
关键说明
- Server Component无需添加
'use client'指令,可直接在组件内部异步请求数据,替代Pages Router中的getServerSideProps。 - 参数通过组件的
props直接获取:params对应动态路由的[slug]值,searchParams对应URL中的查询参数。 - 调用Edamam API时,需要再次对
decodedUri进行编码,确保URL参数格式正确。
内容的提问来源于stack exchange,提问作者user17449555
相关产品推荐
相关产品推荐

