Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BodyInserters.fromMultipartData swallows content type in certain cases #26410

Closed
lazystone opened this issue Jan 20, 2021 · 2 comments
Closed
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Milestone

Comments

@lazystone
Copy link

lazystone commented Jan 20, 2021

Affects: 5.3.3


My use case is that I need to proxy some multipart form to another service:

  @PostMapping(
      consumes = MediaType.MULTIPART_FORM_DATA_VALUE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  @ResponseStatus(HttpStatus.CREATED)
  public Mono<ResponseEntity<SomeEntityId>> upload(
      @RequestBody Mono<MultiValueMap<String, Part>> parts) {
    return someServiceClient.upload(parts);
  }

  public Mono<ResponseEntity<SomeEntityId>> upload(
      Mono<MultiValueMap<String, Part>> partsMono) {
    return partsMono.flatMap(
        parts ->
            webClient
                .post()
                .uri("/api/someservice/v2")
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .accept(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromMultipartData(parts))
                .retrieve()
                .toEntity(SomeEntityId.class)
                .publishOn(Schedulers.boundedElastic())
                .doOnError(e -> LOGGER.error("Couldn't upload media", e)));
  }

Problem here is that BodyInserters.fromMultipartData swallows content type from Part.headers().

As a workaround I convert MultiValueMap before passing it to BodyInserters.fromMultipartData:

  private MultiValueMap<String, org.springframework.http.HttpEntity<?>> mapForm(
      MultiValueMap<String, Part> parts) {
    MultipartBodyBuilder builder = new MultipartBodyBuilder();
    parts.forEach(
        (key, valueList) -> {
          for (Part value : valueList) {
            builder.part(key, value, value.headers().getContentType());
          }
        });

    return builder.build();
  }

I'm not sure how it's actually supposed to work, but perhaps here, apart from context(), is it worth it to copy headers() as well?

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 20, 2021
@sbrannen sbrannen added the in: web Issues in web modules (web, webmvc, webflux, websocket) label Jan 20, 2021
@poutsma poutsma self-assigned this Jan 21, 2021
@poutsma poutsma added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 21, 2021
@poutsma poutsma added this to the 5.3.4 milestone Jan 21, 2021
@AbdelHedhili
Copy link

Hi @poutsma ,
I think this commit (e537844) breaks my server. I'm almost in the same case as the code described above except that I take one FilePart from the incoming request and I rename it in the outgoing request. Something like that:

  public Mono<ResponseEntity<SomeEntityId>> upload(
      @RequestBody Mono<MultiValueMap<String, Part>> parts) {
    return someServiceClient.upload(parts.map(m -> (FilePart) m.get("foo").get(0));    //here I take the foo part 

  }

  public Mono<ResponseEntity<SomeEntityId>> upload(
      Mono<MultiValueMap<String, Part>> partsMono) {
    return multipartFile.flatMap(file -> {
            MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
            multipartBodyBuilder.part("bar", file); //here I rename it to bar
            return webClient.post()
                    .uri("uri")
                    .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA.toString())
                    .body(BodyInserters.fromMultipartData(multipartBodyBuilder.build()))
        });
    }

I came across this issue when upgrading my spring version : when I do this, in the outgoing request, the part that I wanted to rename "bar" is still named "foo". It worked in spring 5.2.8 (it renamed the part) but in 5.3.6 the file is not renamed anymore.

Do you have any advice ?
Thanks.

@poutsma
Copy link
Contributor

poutsma commented May 25, 2021

@AbdelHedhili Please file a separate issue for this problem. Feel free to ping me on said issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants