React表单传图片至API遇Axios 500错误,请求排查
React表单提交Multipart数据时Axios 500错误排查
问题概述
通过React表单提交包含图片的公司数据到API时,触发Axios 500错误,错误信息为Failed to parse multipart servlet request;此前还因缺少createdBy字段出现company not found错误。但使用Postman以form-data格式提交以下3个键值对时可成功创建公司:
keys value logo : logo.png name : Abc Company company : { "websiteUrl":"www.abc.com", "firstName":"John", "lastName":"Doe", "emailId":"john@gmail.com", "phoneNumber":"1122334455", "createdBy":"110" }
代码问题分析
你的React代码存在以下几个关键问题:
FormData结构不匹配
Postman中是将company作为一个完整的JSON对象提交,但你的代码把company对象内的每个字段单独append到FormData,后端无法识别这种拆分的结构,导致multipart请求解析失败。字段名不匹配
Postman中公司对象的邮箱字段是emailId,但你的代码state里定义的是email,会导致后端接收不到正确的邮箱数据,可能引发数据校验错误。未处理logo为空的情况
如果用户不上传logo,formData.logo为null,直接append到FormData会导致请求结构异常。Axios请求头未明确(可选)
虽然FormData会自动设置Content-Type为multipart/form-data,但部分后端需要明确指定该请求头,否则可能解析失败。
修复方案
- 将
company对象序列化为JSON字符串,作为company键的值append到FormData; - 将state和表单中的
email字段改为emailId,与Postman的字段名一致; - 添加logo存在性判断,仅当logo存在时才append到FormData;
- 给Axios请求明确设置
multipart/form-data请求头(可选,但能避免自动设置的潜在问题)。
修复后的代码
关键修改部分
1. State字段调整
将company中的email改为emailId:
const [formData, setFormData] = useState({ logo: null, name: "", company: { websiteUrl: "", firstName: "", lastName: "", emailId: "", // 改为emailId phoneNumber: "", createdBy: "", }, });
2. 表单输入字段调整
将邮箱输入框的name改为emailId:
<input type="email" maxLength={100} className={inputClassName} placeholder="name@company.com" name="emailId" // 改为emailId value={formData.company.emailId} onChange={handleCompanyChange} />
3. 修复handleSubmit函数
const handleSubmit = (e) => { e.preventDefault(); console.log(formData); const companyData = new FormData(); // 仅当logo存在时添加 if (formData.logo) { companyData.append("logo", formData.logo); } companyData.append("name", formData.name); // 将company对象序列化为JSON字符串 companyData.append("company", JSON.stringify(formData.company)); axios .post( "http://localhost:8080/api/v1/company/create/company", companyData, { headers: { 'Content-Type': 'multipart/form-data' // 明确指定请求头 } } ) .then((response) => { console.log(response.data); }) .catch((error) => { console.log(error); }); };
完整修复后的组件代码
import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; import axios from "axios"; const AddCompany = () => { const [formData, setFormData] = useState({ logo: null, name: "", company: { websiteUrl: "", firstName: "", lastName: "", emailId: "", phoneNumber: "", createdBy: "", }, }); const handleChange = (e) => { const { name, value } = e.target; setFormData((prevState) => ({ ...prevState, [name]: value, })); }; const handleLogoChange = (e) => { setFormData((prevState) => ({ ...prevState, logo: e.target.files[0], })); }; const handleCompanyChange = (e) => { const { name, value } = e.target; setFormData((prevState) => ({ ...prevState, company: { ...prevState.company, [name]: value, }, })); }; const handleSubmit = (e) => { e.preventDefault(); console.log(formData); const companyData = new FormData(); if (formData.logo) { companyData.append("logo", formData.logo); } companyData.append("name", formData.name); companyData.append("company", JSON.stringify(formData.company)); axios .post( "http://localhost:8080/api/v1/company/create/company", companyData, { headers: { 'Content-Type': 'multipart/form-data' } } ) .then((response) => { console.log(response.data); }) .catch((error) => { console.log(error); }); }; const inputClassName = "bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"; const labelClassNames = "block mb-2 text-sm font-medium text-gray-900 dark:text-white"; return ( <> <form onSubmit={handleSubmit} encType="multipart/form-data"> <div className="mb-5"> <label className={labelClassNames} htmlFor="logo"> Upload logo <span className=" text-blue-600"> (optional)</span> </label> <input className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400" aria-describedby="file_input_help" type="file" onChange={handleLogoChange} /> <p className="mt-1 text-sm text-gray-500 dark:text-gray-300" id="file_input_help" > SVG, PNG, JPG or GIF (MAX. 800x400px). </p> </div> <div className="grid gap-6 mb-6 md:grid-cols-2"> <div> <label htmlFor="phone" className={labelClassNames}> Company Name <span className=" text-red-600"> (required)</span> </label> <input type="text" maxLength={150} name="name" value={formData.name} onChange={handleChange} className={inputClassName} placeholder="Company Name" required /> </div> <div> <label htmlFor="phone" className={labelClassNames}> Website URL <span className=" text-blue-600"> (optional)</span> </label> <input type="text" maxLength={100} className={inputClassName} placeholder="www.url.com" name="websiteUrl" value={formData.company.websiteUrl} onChange={handleCompanyChange} /> </div> <div> <label htmlFor="first_name" className={labelClassNames}> Contact Person First Name <span className=" text-red-600"> (required)</span> </label> <input type="text" maxLength={50} className={inputClassName} placeholder="First name" name="firstName" value={formData.company.firstName} onChange={handleCompanyChange} required /> </div> <div> <label htmlFor="last_name" className={labelClassNames}> Contact Person Last Name <span className=" text-red-600"> (required)</span> </label> <input type="text" maxLength={50} className={inputClassName} placeholder="Last name" name="lastName" value={formData.company.lastName} onChange={handleCompanyChange} required /> </div> <div className="mb-6"> <label htmlFor="email" className={labelClassNames}> Email address <span className=" text-red-600"> (required)</span> </label> <input type="email" maxLength={100} className={inputClassName} placeholder="name@company.com" name="emailId" value={formData.company.emailId} onChange={handleCompanyChange} required /> </div> <div className="mb-6"> <label htmlFor="phone" className={labelClassNames}> Phone No <span className=" text-blue-600"> (optional)</span> </label> <input type="number" maxLength={50} className={inputClassName} placeholder="+1 123-456-789" name="phoneNumber" value={formData.company.phoneNumber} onChange={handleCompanyChange} /> </div> <div className="mb-6"> <label htmlFor="createdBy" className={labelClassNames}> createdBy <span className=" text-blue-600"> (optional)</span> </label> <input type="number" maxLength={50} className={inputClassName} placeholder="100" name="createdBy" value={formData.company.createdBy} onChange={handleCompanyChange} /> </div> </div> <div className="flex justify-center"> <button type="submit" className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-0 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-60 px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800" > Submit </button> </div> </form> </> ); }; export default AddCompany;
内容的提问来源于stack exchange,提问作者Prince
相关产品推荐
相关产品推荐

