你需要让你的控制器方法使用MediaType.MULTIPART_FORM_DATA_VALUE,
@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
......
您还需要添加MappingJackson2HttpMessageConverter 支持application/octet-stream。在这个答案中,
- 我使用
WebMvcConfigurer#extendMessageConverters 配置它,这样我就可以保留其他转换器的默认配置。(Spring MVC 配置了 Spring Boot 的转换器)。
- 我从 Spring 使用的
ObjectMapper 实例创建转换器。
[详情]
Spring Boot Reference Documentation - Spring MVC Auto-configuration
How do I obtain the Jackson ObjectMapper in use by Spring 4.1?
Why does Spring Boot change the format of a JSON response even when a custom converter which never handles JSON is configured?
@Configuration
public class MyConfigurer implements WebMvcConfigurer {
@Autowired
private ObjectMapper objectMapper;
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
ReadOnlyMultipartFormDataEndpointConverter converter = new ReadOnlyMultipartFormDataEndpointConverter(
objectMapper);
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.addAll(converter.getSupportedMediaTypes());
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
converter.setSupportedMediaTypes(supportedMediaTypes);
converters.add(converter);
}
}
[注意]
您还可以通过扩展来修改转换器的行为。
在这个答案中,我扩展了MappingJackson2HttpMessageConverter 以便
- 仅当映射的控制器方法仅消耗
MediaType.MULTIPART_FORM_DATA_VALUE时才读取数据
- 它不写任何响应(另一个转换器会这样做)。
public class ReadOnlyMultipartFormDataEndpointConverter extends MappingJackson2HttpMessageConverter {
public ReadOnlyMultipartFormDataEndpointConverter(ObjectMapper objectMapper) {
super(objectMapper);
}
@Override
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
// When a rest client(e.g. RestTemplate#getForObject) reads a request, 'RequestAttributes' can be null.
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return false;
}
HandlerMethod handlerMethod = (HandlerMethod) requestAttributes
.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
if (handlerMethod == null) {
return false;
}
RequestMapping requestMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
if (requestMapping == null) {
return false;
}
// This converter reads data only when the mapped controller method consumes just 'MediaType.MULTIPART_FORM_DATA_VALUE'.
if (requestMapping.consumes().length != 1
|| !MediaType.MULTIPART_FORM_DATA_VALUE.equals(requestMapping.consumes()[0])) {
return false;
}
return super.canRead(type, contextClass, mediaType);
}
// If you want to decide whether this converter can reads data depending on end point classes (i.e. classes with '@RestController'/'@Controller'),
// you have to compare 'contextClass' to the type(s) of your end point class(es).
// Use this 'canRead' method instead.
// @Override
// public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
// return YourEndpointController.class == contextClass && super.canRead(type, contextClass, mediaType);
// }
@Override
protected boolean canWrite(MediaType mediaType) {
// This converter is only be used for requests.
return false;
}
}
415 错误的原因
当您的控制器方法使用MediaType.APPLICATION_OCTET_STREAM_VALUE 时,它不会处理带有Content-Type: multipart/form-data; 的请求。因此你得到415。
另一方面,当您的控制器方法使用MediaType.MULTIPART_FORM_DATA_VALUE 时,它可以处理带有Content-Type: multipart/form-data; 的请求。但是,根据您的配置,不处理没有 Content-Type 的 JSON。
当您使用 @RequestPart 注释对方法参数进行注释时,
-
RequestPartMethodArgumentResolver 解析请求。
-
RequestPartMethodArgumentResolver 在未指定时将 content-type 识别为 application/octet-stream。
-
RequestPartMethodArgumentResolver 使用 MappingJackson2HttpMessageConverter 解析请求主体并获取 JSON。
- 默认配置
MappingJackson2HttpMessageConverter只支持application/json和application/*+json。
- (据我阅读您的问题)您的
MappingJackson2HttpMessageConverters 似乎不支持application/octet-stream。(因此您得到415。)
结论
因此,我认为您可以通过让MappingJackson2HttpMessageConverter(HttpMessageConverter 的实现)支持application/octet-stream 来成功处理请求,就像上面一样。
[更新 1]
如果您不需要使用@Valid 注释验证MyModel,而只是想将JSON 正文转换为MyModel,@RequestParam 会很有用。
如果您选择此解决方案,则不必必须配置 MappingJackson2HttpMessageConverter 以支持 application/octet-stream。
使用此解决方案,您不仅可以处理 JSON 数据,还可以处理文件数据。
@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public void test(@RequestParam(value = "MyModel") Part part) throws IOException {
// 'part' is an instance of 'javax.servlet.http.Part'.
// According to javadoc of 'javax.servlet.http.Part',
// 'The part may represent either an uploaded file or form data'
try (InputStream is = part.getInputStream()) {
ObjectMapper objectMapper = new ObjectMapper();
MyModel myModel = objectMapper.readValue(part.getInputStream(), MyModel.class);
.....
}
.....
}
另见
Javadoc of RequestPartMethodArgumentResolver
Javadoc of MappingJackson2HttpMessageConverter
Content type blank is not supported(相关问题)
Spring Web MVC - Multipart