NextJS API路由向DjangoREST后端传图片失败问题排查
问题:Next.js API路由转发PATCH请求更新图片失败
我开发了一个NextJS应用,客户端页面请求API路由,再由API路由向Django后端发起PATCH请求更新图片。图片并未成功更新,但直接用Postman/Insomnia调用后端,或客户端直接调用后端时可正常修改。我收到了后端返回的200响应,但图片字段并未实际更新,推测问题出在客户端到API路由的request.body中的文件处理上。
客户端页面代码
const UpdateImageField = async (event: any) => { event.preventDefault() var formdata = new FormData(); formdata.append("coverfield", event.target.files[0], event.target.files[0]["name"]); const ress = await fetch(`/api/playlist/playlistimage/${playlistediting["id"]}`, { method: 'PATCH', headers: { 'Accept': 'application/json', }, body: formdata }); }
API路由页面代码
export default async (req, res) => { if (req.method === "PATCH") { const { playlistid } = req.query const cookies = cookie.parse(req.headers.cookie ?? ''); const access = cookies.access ?? false; if (access === false) { return res.status(403).json({ error: 'User forbidden from making the request' }); } try { const apiRes = await fetch(`${API_URL}/playlist_api/playlists/${playlistid}/`, { method: 'PATCH', headers: { 'Accept': 'application/json', 'Content-Type': 'multipart/form-data; boundary=---WebKitFormBoundary7MA4YWxkTrZu0gW', 'Authorization': `Bearer ${access}` }, body: req.body }); const data = await apiRes.json(); console.log(data) if (apiRes.status === 200) { return res.status(200).json({ updatedplaylist: data }); } else { return res.status(apiRes.status).json({ error: data.error }); } } catch (err) { return res.status(500).json({ error: 'Something went wrong when changing the image' }); } } }
问题分析与解决方案
问题核心是Next.js默认的请求体解析器不支持multipart/form-data类型,直接传递req.body会导致文件数据丢失,后端收到空表单数据,因此返回200但未实际更新图片。
解决步骤
- 安装表单解析依赖
在项目中安装formidable来处理文件表单:
npm install formidable # 或 yarn add formidable
- 修改API路由代码
禁用Next.js默认的body解析器,用formidable解析请求体后重新构建FormData再转发:
import formidable from 'formidable'; import cookie from 'cookie'; // 禁用Next.js默认的body解析器 export const config = { api: { bodyParser: false, }, }; export default async (req, res) => { if (req.method === "PATCH") { const { playlistid } = req.query; const cookies = cookie.parse(req.headers.cookie ?? ''); const access = cookies.access ?? false; if (access === false) { return res.status(403).json({ error: 'User forbidden from making the request' }); } // 解析multipart/form-data请求体 const form = formidable({ multiples: false }); const [fields, files] = await form.parse(req); // 重新构建FormData const formData = new FormData(); if (files.coverfield) { const file = files.coverfield[0]; formData.append('coverfield', file, file.originalFilename); } try { const apiRes = await fetch(`${process.env.API_URL}/playlist_api/playlists/${playlistid}/`, { method: 'PATCH', headers: { 'Authorization': `Bearer ${access}`, // 不要手动设置Content-Type,浏览器会自动生成带正确boundary的头部 }, body: formData, }); const data = await apiRes.json(); console.log(data); if (apiRes.status === 200) { return res.status(200).json({ updatedplaylist: data }); } else { return res.status(apiRes.status).json({ error: data.error }); } } catch (err) { return res.status(500).json({ error: 'Something went wrong when changing the image' }); } } else { res.setHeader('Allow', ['PATCH']); res.status(405).end(`Method ${req.method} Not Allowed`); } };
- 关键注意事项
- 必须禁用Next.js默认的
bodyParser,否则无法正确解析文件表单; - 转发请求时不要手动设置
Content-Type,手动设置会导致boundary不匹配,后端无法解析; formidable返回的fields和files均为数组类型,需取第一个元素使用。
内容的提问来源于stack exchange,提问作者kprime21
相关产品推荐
相关产品推荐

