最近使用spring boot编写api,使用swagger显示并测试,执行的时候其中某一个api在解析multipart/form-data类型的时候发生了如下错误: Content type 'application/octet-stream' not supported 大概的api代码如下:
@PostMapping(value = "/edit") public void edit(@RequestPart(value = "info", required = false) List<UserEntity> users,@RequestParam(value = "image", required = false)MultipartFile file) { //to do }为什么会发生这种错误呢?首先可以肯定的是并没有设置application/octet-stream这种content-type,那么错误的来源是哪儿呢?通过追踪错误的堆栈信息,找到类AbstractMessageConverterMethodArgumentResolver中的方法readWithMessageConverters:
@SuppressWarnings("unchecked") @Nullable protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { MediaType contentType; boolean noContentType = false; try { contentType = inputMessage.getHeaders().getContentType(); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } if (contentType == null) { noContentType = true; contentType = MediaType.APPLICATION_OCTET_STREAM; } Class<?> contextClass = parameter.getContainingClass(); Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null); if (targetClass == null) { ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); targetClass = (Class<T>) resolvableType.resolve(); } HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null); Object body = NO_VALUE; EmptyBodyCheckingHttpInputMessage message; try { message = new EmptyBodyCheckingHttpInputMessage(inputMessage); for (HttpMessageConverter<?> converter : this.messageConverters) { Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass(); GenericHttpMessageConverter<?> genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) : (targetClass != null && converter.canRead(targetClass, contentType))) { if (message.hasBody()) { HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse)); body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType); } else { body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType); } break; } } } catch (IOException ex) { throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage); } if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); } MediaType selectedContentType = contentType; Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(theBody, !traceOn); return "Read \"" + selectedContentType + "\" to [" + formatted + "]"; }); return body; }该方法在开始的时候取content-type,如果找不到的话就设置为application/octet-stream,然后根据content-type找到可以处理该类型的messageConverter,获取body信息。现在我们可以找到为什么会抛出这样的错误信息了,因为没有定义application/octet-stream相关的messageConverter。 所以我们可以通过自定义一个messageConverter解决这个问题,添加的converter的代码如下:
@Component public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter { protected MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) { super(objectMapper, MediaType.APPLICATION_OCTET_STREAM); } @Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return false; } @Override public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) { return false; } @Override protected boolean canWrite(MediaType mediaType) { return false; } }问题虽然解决了,但是,为什么content-type会是null呢? 我们使用swagger调用的api,查看swagger生成的curl命令发现,请求中image这个参数指定了content-type,但是info却并没有: curl -X POST "http://127.0.0.1:8080/api/v1.0/edit" -H "accept: */*" -H "Content-Type: multipart/form-data" -F "image=@1.jpg;type=image/jpeg" -F "info=[]", 然后如果给info添加上content-type,即修改为curl -X POST "http://127.0.0.1:8080/api/v1.0/edit" -H "accept: */*" -H "Content-Type: multipart/form-data" -F "image=@1.jpg;type=image/jpeg" -F "info=[];type=application/json"不添加messageConverter也可以正常运行,所以这应该是swagger的问题,暂时没有找到配置swagger的好办法,所以干脆选择了添加messageConverter来解决上述问题,mark一下。