Spring Boot无法解析Multipart请求:Content-Type不支持问题排查
问题排查:前端Multipart表单请求Spring Boot接口报错Content-Type不支持
问题现象
前端提交带附件的表单时,调用后端Spring Boot接口触发如下错误:
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content-Type 'multipart/form-data;boundary=----WebKitFormBoundaryn1nNr7iOEen0Aykw' is not supported]
Postman测试后端接口功能正常,前端请求第三方测试接口也能得到正常响应,怀疑后端Controller写法存在问题,请求协助排查。
相关代码
前端JS代码
async function submitDataToBackend(data, file, formDataa) { const formatedFormData = dataFormattingIntoJSON(data); const formData = new FormData(); formData.append('form', formatedFormData); if (file) { formData.append('CV', file); } for (const pair of formData.entries()) { console.log('key: ', pair[0], '; value: ', pair[1]); } const headers = new Headers(); headers.append("Content-Type", "multipart/form-data"); fetch('http://localhost:8081/api/v1/forms/newproject', { method: 'POST', headers: headers, body: formData, }) .then((response) => response.json()) .then((data) => { console.log('response from server: ', data); }) .catch((error) => { console.log(error); }); }
后端Controller代码
@RestController @RequiredArgsConstructor @RequestMapping("/api/v1/forms") @Tag(name = "Forms Controller", description = "Forms API") @Slf4j public class FormsController { private final EmailServiceMarsNet emailServiceMarsNet; @PostMapping(value="/newproject",consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}) @Operation(summary = "Method for send email form 'Start your NEW project'") public StartProjectDTO sendStartProjectRequest(@RequestPart(value = "form") @Valid StartProjectDTO request, @RequestPart(value = "CV", required = false) MultipartFile file){ log.info(request.toString()); emailServiceMarsNet.sendFormStartYourProjectNew(request,file); return request; } }
问题分析与解决方案
核心问题1:前端手动设置Content-Type导致boundary失效
当使用FormData作为fetch请求的body时,浏览器会自动生成包含正确boundary的Content-Type头。前端手动设置Content-Type: multipart/form-data会覆盖浏览器自动生成的头,丢失boundary信息,导致后端无法正确解析multipart格式的请求体,从而触发HttpMediaTypeNotSupportedException。
核心问题2:form字段的JSON数据格式可能无法被后端解析
如果dataFormattingIntoJSON(data)返回的是JSON对象而非字符串,直接通过formData.append('form', formatedFormData)添加的话,浏览器会将其转换为[object Object]字符串,后端无法将该字符串反序列化为StartProjectDTO对象。
修复后的前端代码
async function submitDataToBackend(data, file, formDataa) { const formatedFormData = dataFormattingIntoJSON(data); const formData = new FormData(); // 确保form字段是带正确媒体类型的JSON内容 const formBlob = new Blob([JSON.stringify(formatedFormData)], { type: 'application/json' }); formData.append('form', formBlob); if (file) { formData.append('CV', file); } for (const pair of formData.entries()) { console.log('key: ', pair[0], '; value: ', pair[1]); } // 移除手动设置的Content-Type,由浏览器自动生成 fetch('http://localhost:8081/api/v1/forms/newproject', { method: 'POST', body: formData, }) .then((response) => response.json()) .then((data) => { console.log('response from server: ', data); }) .catch((error) => { console.log(error); }); }
后端代码确认
后端Controller的写法是正确的:
@PostMapping指定了consumes = MediaType.MULTIPART_FORM_DATA_VALUE,明确接收multipart类型请求@RequestPart用于接收multipart请求中的不同部分,form对应JSON格式的DTO,CV对应可选的文件
内容的提问来源于stack exchange,提问作者ObserverUA
相关产品推荐
相关产品推荐

