Вам нужно, чтобы ваш метод контроллера потреблял 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 - Spring MVC Автоконфигурация
Как получить Jackson ObjectMapper, используемый Spring 4.1?
Почему Spring Boot изменяет формат ответа JSON, даже если пользовательский преобразователь никогда не выполняет ручки JSON настроены?
@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;
. Однако JSON без Content-Type
не обрабатывается в зависимости от вашей конфигурации.
Когда вы аннотируете аргумент метода аннотацией @RequestPart
,
RequestPartMethodArgumentResolver
анализирует запрос. RequestPartMethodArgumentResolver
распознает тип содержимого как application/octet-stream
, если он не указан. RequestPartMethodArgumentResolver
использует MappingJackson2HttpMessageConverter
для анализа тела запроса и получения JSON. - По умолчанию
MappingJackson2HttpMessageConverter
поддерживает только application / json и application / * + json. - (насколько я прочитал ваш вопрос) Ваши
MappingJackson2HttpMessageConverter
s не кажутся поддержать application/octet-stream
. (следовательно, вы получите 415
.)
Заключение
Поэтому я думаю, что вы можете успешно справиться запрос, позволяющий MappingJackson2HttpMessageConverter
(реализация HttpMessageConverter
) поддерживать application/octet-stream
, как указано выше.
[ОБНОВЛЕНИЕ 1]
Если вам не нужно проверять MyModel
с аннотацией @Valid
и просто хотите преобразовать 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);
.....
}
.....
}
См. также
Javado c RequestPartMethodArgumentResolver
Javado c из MappingJackson2HttpMessageConverter
Пустой тип содержимого не поддерживается (Смежный вопрос)
Spring Web MVC - Multipart