java Spring 无法处理“multipart/form-data”POST 请求(错误 400“Bad request”)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/46517583/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 09:14:51  来源:igfitidea点击:

Spring cannot process “multipart/form-data” POST request (error 400 “Bad request”)

javaspring

提问by Maxim

when I post request to this server code - everething works good:

当我向此服务器代码发布请求时 - 一切都很好:

@RequestMapping(method = RequestMethod.POST, consumes = { "application/json" })
public ResponseEntity<String> addQuestion(@RequestBody String dtoObject) { ... }

but if I change request to "multipart/form-data" - Spring returns error 400 "Bad request":

但是如果我将请求更改为“multipart/form-data” - Spring 返回错误 400“错误请求”:

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<String> addQuestion(@RequestBody String dtoObject) { ... }

Why? May be I should create some extra bean?

为什么?可能是我应该创建一些额外的豆子?

PS: I need "multipart/form-data" for sending files together with json objects.

PS:我需要“multipart/form-data”来将文件与 json 对象一起发送。

回答by Chi Dov

I think you can't deserialize the file to that dtoObject within the request body. you will need to use @RequestPart to do that.

我认为您不能在请求正文中将文件反序列化为该 dtoObject。你需要使用@RequestPart 来做到这一点。

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
public ResponseEntity<String> addQuestion2(@RequestPart("question") QuestionPostDto dtoObject, @RequestPart("file") MultiPartFile file)  { ... }

your request need to be formdata: with the file you want to upload and json format file question.json

您的请求必须是 formdata: 带有您要上传的文件和 json 格式的文件 question.json

here is my payload example from post man

这是我来自邮递员的有效载荷示例

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="QLbLFIR.gif"
Content-Type: image/gif


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="question"; filename="question.json"
Content-Type: application/json


------WebKitFormBoundary7MA4YWxkTrZu0gW--

or if you don't want to pass a json format file you can pass it with a normal string

或者,如果您不想传递 json 格式的文件,则可以使用普通字符串传递它

@RequestMapping(method = RequestMethod.POST, consumes = { "multipart/form-data" })
    public ResponseEntity<String> addQuestion2(String question, @RequestPart("file") MultiPartFile file)  {
    QuestionPostDto dtoObject = new ObjectMapper().readValue(request, QuestionPostDto.class); 
    // do sth
}

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="QLbLFIR.gif"
Content-Type: image/gif


------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="request"

{
    "key": "value"
}
------WebKitFormBoundary7MA4YWxkTrZu0gW--

see this thread for more detail: Spring MVC Multipart Request with JSON

有关更多详细信息,请参阅此线程: Spring MVC Multipart Request with JSON

回答by Maxim

I solved this problem.

我解决了这个问题。

1) I created the HttpMessageConverter, wich converts json to my custom type QuestionPostDto:

1)我创建了 HttpMessageConverter,将 json 转换为我的自定义类型 QuestionPostDto:

public class QuestionPostDtoHttpMessageConverter implements HttpMessageConverter<QuestionPostDto> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return QuestionPostDto.class == clazz;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.MULTIPART_FORM_DATA);
        return list;
    }

    @Override
    public QuestionPostDto read(Class<? extends QuestionPostDto> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        InputStream istream = inputMessage.getBody();
        String requestString = IOUtils.toString(istream, "UTF-8");

        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(requestString, QuestionPostDto.class);
    }

    @Override
    public void write(QuestionPostDto t, MediaType contentType, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
    }
}

2) I create bean for this type of HttpMessageConverter (we use Spring Boot in project):

2)我为这种类型的 HttpMessageConverter 创建了 bean(我们在项目中使用 Spring Boot):

@Configuration
public class HttpConfiguration {
...
    @Bean
    public QuestionPostDtoHttpMessageConverter commonsMultipartResolver() {
        return new QuestionPostDtoHttpMessageConverter();
    }
}

3) Now my code in RestController works fine:

3)现在我在 RestController 中的代码工作正常:

@RestController
@RequestMapping("/api/question")
@ConfigurationProperties(prefix = "question")
@RequiredArgsConstructor
@Slf4j
public class QuestionController {
...
@PostMapping
ResponseEntity<String> addQuestion(@RequestPart("dtoObject") QuestionPostDto dtoObject, @RequestPart("file") MultipartFile file) { ... }

Thanks to everybody, especially Chi Dov

感谢大家,尤其是Chi Dov