React向Spring Boot提交JSON+文件表单数据报错排查
React表单提交Spring Boot接口出现
application/octet-stream不支持错误的解决 问题背景
基于React.js MFE和Spring Boot微服务开发应用,React表单包含多个输入字段及6个待上传文件,提交至Spring Boot REST控制器时返回Content type 'application/octet-stream' not supported错误,但使用Postman提交相同数据可正常运行。
React提交代码
const handleSubmit = (e) => { e.preventDefault(); setErrorMessage(null); setErrorMessageForiEMessaggi(null); if (selectedFori.length == 0 || selectedMaterie.length == 0) { setErrorMessageForiEMessaggi( "Bisogna selezionare almeno un foro e una materia" ); } else { const data = new FormData(); //aggiungo i valori delle select const formDataAggiornato = { ...formData, ordine: selectedOrdine, regioneSedeStudio: selectedRegioneSedeStudio, provinciaSedeStudio: selectedProvinciaSedeStudio, comuneSedeStudio: selectedComuneSedeStudio, materia1: selectedMaterie.length >= 1 ? selectedMaterie[0] : null, materia2: selectedMaterie.length >= 2 ? selectedMaterie[1] : null, materia3: selectedMaterie.length == 3 ? selectedMaterie[2] : null, foro1: selectedFori.length >= 1 ? selectedFori[0] : null, foro2: selectedFori.length >= 2 ? selectedFori[1] : null, foro3: selectedFori.length == 3 ? selectedFori[2] : null, }; for (const [key, file] of Object.entries(prevFiles)) { if (file) { const newFileName = file?.name ? `${key}_${file.name}` : `${key}_${file}`; console.log("nome file", newFileName); const renamedFile = new File([file], newFileName, { type: file.type, }); data.append(`files`, renamedFile); } } data.append("avvocato", JSON.stringify(formDataAggiornato)); const requestOptions = { headers: { Authorization: `Bearer ${token}` }, method: "POST", body: data, }; // Effettua la richiesta POST utilizzando fetch fetch(url + API_PATH, requestOptions).then((response) => { if (response.ok) { console.log(response); //chiudo modale toggleModal(!isOpen); //svuoto domanda setDomanda(null); //ricarico lista domande reloadListaDomande(); //stampo messaggio successo in TableDomanda setSuccessMessageTable( domanda?.id ? "Modifica avvenuta con successo!" : "Elemento creato correttamente" ); setErrorMessage(null); setErrorMessageForiEMessaggi(null); return response.json(); } else { setErrorMessage( "Errore durante il salvataggio dei dati, ricaricare la pagina" ); console.log(response); } }); } };
Spring Boot控制器代码
@Operation(summary = "salva l'avvocato", tags = {"Avvocati"}) @PreAuthorize("hasRole('userfo')") @PostMapping(value = "", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE}) @ApiResponse(responseCode = "201") public ResponseEntity<AvvocatoDto> salvaAvvocato(@RequestPart("avvocato") @Valid AvvocatoDto dto, @RequestPart(value = "files", required = false) List<MultipartFile> files) throws IOException, ForbiddenOpException {
错误信息
2024-07-15 13:06:33.692 ERROR 1 --- [nio-8081-exec-5] c.c.a.exception.ExceptionsHandler : GENERIC ERROR : Content type 'application/octet-stream' not supported org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:211) ~[spring-webmvc-5.3.27.jar!/:5.3.27] at org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver.resolveArgument(RequestPartMethodArgumentResolver.java:140) ~[spring-webmvc-5.3.27.jar!/:5.3.27] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:122) ~[spring-web-5.3.27.jar!/:5.3.27] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:179) ~[spring-web-5.3.27.jar!/:5.3.27] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:146) ~[spring-web-5.3.27.jar!/:5.3.27] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.27.jar!/:5.3.27] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.27.jar!/:5.3.27] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.27.jar!/:5.3.27]
问题原因
核心问题在于React端添加avvocato字段时,直接传递JSON字符串但未指定Content-Type,导致Spring Boot无法识别该字段的格式,默认将其判定为application/octet-stream,而控制器期望接收的是JSON类型的AvvocatoDto对象。
Postman提交正常是因为Postman会自动为JSON类型的表单字段设置Content-Type: application/json请求头,但React的FormData.append()方法默认不会为字符串类型的JSON数据添加该头信息。
另外,文件处理部分若file.type为空,新创建的File对象会缺失类型标识,也可能触发类似的类型识别错误。
解决方案
1. 为JSON字段指定正确的Content-Type
修改React代码中添加avvocato字段的逻辑,使用Blob包装JSON字符串并明确指定MIME类型:
// 替换原有的data.append("avvocato", JSON.stringify(formDataAggiornato)); const avvocatoBlob = new Blob([JSON.stringify(formDataAggiornato)], { type: 'application/json' }); data.append("avvocato", avvocatoBlob);
2. 为文件设置默认类型(可选)
若存在文件类型为空的情况,为新创建的File对象添加默认类型,避免类型缺失:
const renamedFile = new File([file], newFileName, { type: file.type || 'application/octet-stream', // 补充默认类型 });
验证修改
完成上述修改后重新提交表单,Spring Boot就能正确识别avvocato字段为JSON类型,同时文件的Content-Type也能正常传递,即可解决该错误。
内容的提问来源于stack exchange,提问作者Andrea
相关产品推荐
相关产品推荐

