上传图片文件时请求无限处于pending状态问题排查
上传包含图片、视频文件的文章到数据库时,全流程无报错抛出,但请求始终停留在pending状态,无响应返回。
相关代码片段
前端表单提交逻辑
const [formdata, setFormdata] = useState( { ressort:"", theme:"", title:"", content:"", } ) const {ressort,theme,title,content} = formdata; const [fileData, setFileData] = useState({ img:"" }) const {img} = fileData; const fileInput = useRef(img); const fileChange = (e)=>{ console.log(fileInput.current.files); setFileData(fileInput.current.files[0]) } console.log(fileData); //works const handleChange = (e)=>{ setFormdata((prevState)=>({ ...prevState, [e.target.name]: e.target.value })) } const onSubmit = (e)=>{ e.preventDefault(); const data = new FormData(); data.append("img", fileData); console.log(data); const mainnewsData ={ data, ressort, theme, title, content } console.log(mainnewsData); dispatch(createMainNews(mainnewsData)); }
Redux异步Thunk逻辑
export const createMainNews = createAsyncThunk("mainNews/create", async (mainnewsData, thunkAPI)=>{ try{ const token = thunkAPI.getState().auth.user.token; return await mainnewsService.createMainNews(mainnewsData, token); } catch(error){ const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString() return thunkAPI.rejectWithValue(message); } })
服务层Axios请求代码
const API_URL = "http://localhost:5000/api/mainNews/"; const createMainNews = async (mainnewsData, token)=>{ const config = { headers: { "Content-Type": "multipart/form-data", "token": `Bearer ${token}` }, } const response = await axios.post(API_URL, mainnewsData, config); return response.data; }
后端接口实现(基于Express+Multer)
router.post("/", verifyTokenAndAuthorization,upload.single("img"), async (req,res)=>{ const newMainNews = new MainNews({ img: req.file.originalname, ressort: req.body.ressort, theme: req.body.theme, title: req.body.title, content:req.body.content, }); try{ const savedMainNews = await newMainNews.save(); res.status(200).json(savedMainNews); } catch(error){ res.status(403) throw new Error("Aktion fehlgeschlagen"); } });
请求卡pending是前后端传参、解析、响应逻辑的多个问题共同导致的,逐个修复即可:
FormData传参结构错误
把FormData实例嵌套在普通对象mainnewsData里,和其他文本字段平级传递时,Axios无法正确识别并序列化FormData内容,后端Multer拿不到文件、普通字段也解析失败。
修复方式:所有文本字段和文件都要追加到同一个FormData实例中,直接传递FormData实例,不要套额外的普通对象。
修正后的提交逻辑:const onSubmit = (e)=>{ e.preventDefault(); const data = new FormData(); // 追加文件 data.append("img", fileData); // 所有文本字段统一追加到FormData data.append("ressort", ressort); data.append("theme", theme); data.append("title", title); data.append("content", content); // 直接传FormData实例 dispatch(createMainNews(data)); }顺带修正fileData初始值的冗余问题,初始值直接设为null即可,不需要嵌套对象:
const [fileData, setFileData] = useState(null)手动设置multipart/form-data请求头导致解析失败
multipart/form-data请求头必须携带浏览器自动生成的boundary分隔符,手动写死该请求头会导致分隔符丢失,后端无法正确切割请求体内容,会一直等待完整数据传入。
修复方式:删除手动配置的Content-Type头,Axios在识别到传递的是FormData实例时,会自动设置正确的请求头和boundary。
修正后的请求配置:const createMainNews = async (mainnewsData, token)=>{ const config = { headers: { // 删除手动写的Content-Type配置 "Authorization": `Bearer ${token}` // 建议使用标准Authorization头传递JWT,若后端自定义读取token字段可保留原配置 }, } const response = await axios.post(API_URL, mainnewsData, config); return response.data; }后端错误处理不返回响应,导致请求挂起
现有后端代码把req.file.originalname等字段读取逻辑放在了try块外面,一旦Multer解析失败、req.file为undefined,会直接抛出同步错误;同时catch块内只设置了HTTP状态码,没有返回任何响应内容,前端会一直等待后端返回直到超时。
修复方式:把所有业务逻辑移入try块,所有异常分支都要明确返回响应。
修正后的后端路由:router.post("/", verifyTokenAndAuthorization,upload.single("img"), async (req,res)=>{ try{ // 先校验文件是否上传成功 if(!req.file) { return res.status(400).json({message: "文件上传失败"}); } const newMainNews = new MainNews({ img: req.file.originalname, ressort: req.body.ressort, theme: req.body.theme, title: req.body.title, content:req.body.content, }); const savedMainNews = await newMainNews.save(); res.status(200).json(savedMainNews); } catch(error){ // 错误场景必须返回响应,不能只设置状态码 res.status(500).json({message: "操作失败"}) } });
内容的提问来源于stack exchange,提问作者Roman

