使用 json 和文件发送表单数据以响应 Spring Boot 时出错

问题描述 投票:0回答:1

我们正在使用react.js MFE和Spring Boot微服务开发微服务应用程序

我有一个反应表单,其中有几个要填写的字段和要上传的 6 个文件,我将其发布到 Spring Boot Rest 控制器。

这是我们用来提交表单的 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(`files`, prevFiles);

      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);
        }
      });
    }
  };

这是保存数据和文件的其余控制器

  @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]

我哪里做错了?

reactjs spring-boot multipartform-data
1个回答
0
投票

他们工作得怎么样?

首先,在客户端,当您使用文件获取发布表单数据时,将设置

Content-Type
,如
multipart/form-data; boundary=----....

在 spring 端,您的请求将通过

resolveArgument
中的方法
org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
解决,并且无法在第一个参数处将文本转换为
AvvocatoDto

深深地

解析器在尝试获取第一个参数的内容类型并设置为

null
(您在错误日志中看到)时收到
"application/octet-stream"
,之后没有转换器可以从
application/octet-stream
转换为
AvvocatoDto
类。更多详情请参阅
AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters

如何处理?

解决方案1

您可以创建模型类来保存所有字段和文件,请参阅这篇文章

解决方案2

将第一个参数更改为字符串

@RequestPart("avvocato") String avvocatoStr

并手动反序列化和验证它。

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.