【问题标题】:Spring cannot process “multipart/form-data” POST request (error 400 “Bad request”)Spring 无法处理“multipart/form-data”POST 请求(错误 400“Bad request”)
【发布时间】:2018-03-13 01:21:21
【问题描述】:

当我向此服务器代码发布请求时 - 一切正常:

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

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

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

为什么? 可能我应该创建一些额外的 bean 吗?

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

【问题讨论】:

  • 如果您使用 Postman 之类的工具来测试您的 REST 服务路由,则必须在该工具中将 Content-typeheader 键分配为 multipart/form-data
  • 在 Postman 中我得到相同的结果(错误 400“错误请求”)
  • 请参考您的控制台并阅读此异常的整个堆栈跟踪,您可能知道此错误来自何处。
  • mustabelMo: 对不起,可能我不明白你:错误 400 “Bad request” 不是例外,它是服务器响应的代码,所以我没有它的堆栈跟踪.
  • 一般发生异常时,我们可以在IDE控制台看到stackTrace的日志

标签: java spring


【解决方案1】:

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

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

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

这是我的来自 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--

或者如果你不想传递一个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--

更多详情请参阅此主题: Spring MVC Multipart Request with JSON

【讨论】:

  • 如果我尝试发布字符串 - 我收到错误 400“错误请求”(已编辑问题)。 @RequestPart - 适用于字符串,但不适用于我的自定义类型 QuestionPostDto(Spring 返回错误 415“不支持的媒体类型”)。
  • 您是否使用邮递员来测试您的请求,如果是,我可以查看您的请求负载吗?
  • Chi Dov,感谢您提供的信息。你是对的 - 我不得不使用@RequestPart。请参阅下面的答案。
【解决方案2】:

我解决了这个问题。

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)我为这种类型的HttpMessageConverter创建bean(我们在项目中使用Spring Boot):

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

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) { ... }

感谢大家,尤其是Chi Dov

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-30
    • 2017-05-08
    • 2019-10-22
    • 1970-01-01
    • 2021-10-11
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    相关资源
    最近更新 更多