你需要让你的控制器方法消耗MediaType.MULTIPART_FORM_DATA_VALUE
,
@PostMapping(value = "/test", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
......
您还需要添加一个MappingJackson2HttpMessageConverter
支持application/octet-stream
。在这个答案中,
- 我通过使用配置它
WebMvcConfigurer#extendMessageConverters
这样我就可以保留其他转换器的默认配置。(Spring MVC 使用 Spring Boot 的转换器进行配置)。
- 我从创建转换器
ObjectMapper
Spring 使用的实例。
[了解更多信息]
Spring Boot 参考文档 - Spring MVC 自动配置 https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-spring-mvc-auto-configuration
如何获取 Spring 4.1 使用的 Jackson ObjectMapper? https://stackoverflow.com/questions/30060006/how-do-i-obtain-the-jackson-objectmapper-in-use-by-spring-4-1
为什么即使配置了从不处理 JSON 的自定义转换器,Spring Boot 也会更改 JSON 响应的格式? https://stackoverflow.com/questions/60462767/why-does-spring-boot-change-the-format-of-a-json-response-even-when-a-custom-con
@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);
}
}
[NOTE]
您还可以通过扩展转换器来修改转换器的行为。
在这个答案中,我扩展了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
errors
当你的控制器方法消耗时MediaType.APPLICATION_OCTET_STREAM_VALUE
,它不处理请求Content-Type: multipart/form-data;
。因此你得到415
.
另一方面,当你的控制器方法消耗MediaType.MULTIPART_FORM_DATA_VALUE
,它可以处理请求Content-Type: multipart/form-data;
。然而 JSON 没有Content-Type
根据您的配置不进行处理。
当您用注释方法参数时@RequestPart
注解,
-
RequestPartMethodArgumentResolver
解析一个请求。
-
RequestPartMethodArgumentResolver
将内容类型识别为application/octet-stream
当没有指定时。
-
RequestPartMethodArgumentResolver
uses a MappingJackson2HttpMessageConverter
解析请求正文并获取 JSON。
- 默认配置
MappingJackson2HttpMessageConverter
仅支持 application/json 和 application/*+json。
- (据我读到你的问题)你的
MappingJackson2HttpMessageConverter
似乎不支持application/octet-stream
.(因此你得到415
.)
结论
因此我认为你可以通过让MappingJackson2HttpMessageConverter
(一个实现HttpMessageConverter
) 支持application/octet-stream
像上面一样。
[更新1]
如果您不需要验证MyModel
with @Valid
注释并且只想将 JSON 主体转换为MyModel
, @RequestParam
可能有用。
如果您选择此解决方案,您就可以NOT必须配置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);
.....
}
.....
}
See Also
RequestPartMethodArgumentResolver 的 Javadoc https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.html
MappingJackson2HttpMessageConverter 的 Javadoc https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/http/converter/json/MappingJackson2HttpMessageConverter.html
不支持空白内容类型 https://stackoverflow.com/questions/60207486/content-type-blank-is-not-supported/60213085#60213085(相关问题)
Spring Web MVC - 多部分 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-multipart-forms