spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.23k stars 40.7k forks source link

POST Multipart JSON + file, DTO not filling #35208

Closed elegos closed 1 year ago

elegos commented 1 year ago

Dear Spring boot team,

recently I had the need to create POST requests with content type multipart/form-data. In particular I have the need to load a file and the relative metadata.

I've created a very simple project to demonstrate this, which you can find attached. I propose the controller right here:

package com.example.demo.controller;

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.example.demo.resource.SampleResource;

import io.swagger.v3.oas.annotations.Operation;

@RestController
@RequestMapping("/demo")
public class AlphaController {
    @Operation(description = "Example")
    @PostMapping(path = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<?> whatever(
        @RequestPart(name = "meta")
        @ModelAttribute
        SampleResource meta,

        @RequestPart(name = "my_file")
        MultipartFile myFile
    ) {
        System.out.println(meta); // SampleResource(foo=null, bar=null)
        System.out.println(myFile); // org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@249d276f

        return null;
    }
}

And this is SampleResource:

package com.example.demo.resource;

import lombok.Data;

@Data
public class SampleResource {
    private String foo;
    private Integer bar;
}

The call is being made with curl, as follows:

curl -X 'POST' \
  'http://localhost:8888/demo/' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'meta={
  "foo": "string",
  "bar": 0
}' \
  -F 'my_file=@my_file.ext'

Here is the project that you can build and run reproducing the problem: multipart_bug.tar.gz

The sample project is using SpringBoot 2.7.11, though my target application is running SpringBoot 2.5.4 (being part of an older application).

The expected result should be that metadata should be filled with {foo: "string", bar: 0}

Thank you

wilkinsona commented 1 year ago

It looks like you're trying to create a SampleResource from json. To do so, you don't need @ModelAttribute on the controller method's parameter. You also need to send a request that indicates that the part is json. Assuming you've created a file named meta.json with the require contents, one way to do so with curl is the following:

$ curl -X POST http://localhost:8888/demo/ -F 'meta=@meta.json;type=application/json' -Fmy_file=@my_file.ext

If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.