JSON输入意外结束报错排查:旧数据详情页无法加载问题
问题
我遇到了Uncaught (in promise) Syntax Error: Unexpected end of JSON input报错,目前通过GET请求获取数据并在详情页展示。新创建的条目能正常显示,但JSON文件中预先编写的旧数据,虽能被服务端加载、getAll接口也能获取,进入详情页却触发报错无法展示;应用启动后新增的条目则完全正常。
相关代码
recipeService 中的 getOne 方法
export const getOne = async (recipeId) => { return await fetch(`${baseUrl}/recipes/${recipeId}`) .then(res => res.json()) }
recipeService 中的 getAll 方法
export const getAll = async () => { let response = await fetch(`${baseUrl}/recipes`) let recipes = await response.json(); let result = Object.values(recipes); return result; }
Details 组件代码
import "./Details.css"; import { Link, NavLink, useParams,useNavigate } from 'react-router-dom'; import { BrowserRouter,Route,Routes, Switch, Redirect } from 'react-router-dom'; import React, { useEffect, useLayoutEffect, useState } from 'react'; import * as recipeService from "../../../services/recipeService"; const Details = () => { const [recipe,setRecipe] = useState({}); const {recipeId} = useParams(); const recipesd = async () => { let reciperes = await recipeService.getOne(recipeId); setRecipe(reciperes); }; useEffect(() => { recipesd(recipe); },[]); const history = useNavigate(); useLayoutEffect(() => { window.scrollTo(0, 0) }); const scrollToTop = () => { window.scrollTo(0, 0) } return ( <div> <section className="sec11"> <h1>{recipe.name}</h1> <button smooth="true" className="leave" onClick={()=> history(-1)}><i class="fas fa-solid fa-xmark"></i></button> <article className="main"> <article className="artsm"> <h4>Ingredients</h4> <p className="ingre"> {recipe.ingredients} </p> </article> <article className="artm"> <img src={recipe.imageUrl}/> <hr/> <h4>{recipe.name}</h4> <p>Cooking Time: {recipe.time}min</p> <p className="cut-text">{recipe.instructions}</p> </article> <article className="artsm"> <h4>Instructions</h4> <p className="inst"> {recipe.instructions} </p> </article> </article> <article className="comment"> <form className="form"> <textarea name="comment" placeholder=" Comment..."></textarea> <input className="btncom" type="submit" value="Comment"/> </form> </article> <button className="gob" onClick={()=> history(-1)}><i class="fas fa-solid fa-arrow-left-long"></i></button> </section> <section className="sec5navl"> <ul> <li><Link smooth= "true" to="/"><i className="fas fa-solid fa-book-bookmark"></i></Link>Home</li> <li><Link smooth= "true" to="/recipes"><i className="fas fa-thin fa-bowl-rice"></i></Link>Recipes</li> <li><Link smooth= "true" to="/personal-list"><i className="fas fa-solid fa-list-ul"></i></Link>Your Recipes</li> <li><Link smooth= "true" to="/favourites"><i className="fa-solid fa-heart"></i></Link>Favourites</li> <li><Link smooth= "true" to="/create-recipe"><i className="fas fa-solid fa-plus"></i></Link>Add Recipe</li> <li><Link smooth= "true" to="/user-profile"><i className="fas fa-solid fa-user"></i></Link>Your Profile</li> <li><Link smooth= "true" onClick={scrollToTop} to={`/recipe-details/${recipe._id}`}><i class="fas fa-solid fa-arrow-up"></i></Link>Up</li> </ul> </section> </div> ); } export default Details;
预先定义的JSON数据片段
[ { "_id": "62d6be17360d7443f08639d7", "name": "Caponata pasta", "time": "20", "imageUrl": "https://images.immediate.co.uk/production/volatile/sites/30/2020/08/caponata-pasta-a0027c4.jpg?resize=960,872?quality=90&webp=true&resize=375,341", "ingredients": "4 tbsp olive oil 1 large onion, finely chopped 4 garlic cloves, finely sliced 250g chargrilled Mediterranean veg (peppers and aubergines, if possible) from a jar, pot or deli counter, drained if in oil (you can use this oil in place of the olive oil and roughly chopped 400g can chopped tomatoes 1 tbsp small capers 2 tbsp raisins 350g rigatoni, penne or another short pasta shape bunch basil leaves, picked parmesan (or vegetarian alternative), shaved, to serve", "instructions": "STEP 1 Heat the oil in a large pan and cook the onion for 8-10 mins until starting to caramelise (or for longer if you have time – the sweeter the better). Add the garlic for the final 2 mins of cooking time. STEP 2 Tip in the mixed veg, tomatoes, capers and raisins. Season well and simmer, uncovered, for 10 mins, or until you have a rich sauce. STEP 3 Meanwhile, boil the kettle. Pour the kettleful of water into a large pan with a little salt and bring back to the boil. Add the pasta and cook until tender with a little bite, then drain, reserving some of the pasta water. Tip the pasta into the sauce, adding a splash of pasta water if it needs loosening. Scatter with the basil leaves and parmesan, if you like, and serve straight from the pan", "createdAt": "2022-07-19T14:22:15.446Z", "updatedAt": "2022-07-19T14:22:15.446Z" } ]
解决方案
1. 先排查服务端返回内容
Unexpected end of JSON input本质是请求返回的不是有效JSON(甚至是空内容)。先修改getOne方法,打印请求状态和原始返回内容:
export const getOne = async (recipeId) => { const res = await fetch(`${baseUrl}/recipes/${recipeId}`); console.log('请求状态码:', res.status); console.log('原始返回内容:', await res.text()); // 先看文本内容,不要直接转JSON return res.json(); }
如果状态码是404,说明服务端找不到旧数据对应的ID;如果是200但内容为空/非JSON,直接定位服务端接口问题。
2. 检查旧数据的ID匹配度
确认详情页路由中的recipeId和旧数据的_id完全一致(比如62d6be17360d7443f08639d7),避免传错ID导致服务端返回空内容。
3. 排查服务端接口逻辑差异
getAll能拿到旧数据但getOne不行,大概率是服务端接口逻辑问题:
- 旧数据存储为数组格式,而新数据存储为对象键值对(
getAll中用了Object.values(recipes),说明返回的是对象转数组) - 服务端
getOne接口可能只处理对象格式的数据,没做数组遍历查找的逻辑,导致旧数据无法通过ID匹配返回。
4. 前端添加错误处理
不管服务端问题,先给前端加错误捕获,避免报错中断页面:
export const getOne = async (recipeId) => { try { const res = await fetch(`${baseUrl}/recipes/${recipeId}`); if (!res.ok) throw new Error(`请求失败: ${res.status}`); return await res.json(); } catch (err) { console.error('获取单条数据失败:', err); return {}; // 返回空对象避免页面报错 } }
同时在Details组件中判断返回值:
const recipesd = async () => { let reciperes = await recipeService.getOne(recipeId); if (reciperes._id) { // 确认数据有效再更新状态 setRecipe(reciperes); } else { console.log('未找到对应食谱数据'); // 可添加跳转404页面或用户提示逻辑 } };
5. 验证路由参数传递
确认列表页跳转到详情页时,传递的参数是旧数据的_id,比如链接格式为/recipe-details/${item._id},避免传错字段导致请求错误。
内容的提问来源于stack exchange,提问作者DimitriTfb
相关产品推荐
相关产品推荐

