Spring Controller如何处理application/octet-stream第三方回调请求
问题根因
出现HttpMediaTypeNotSupportedException: Content type 'application/octet-stream' not supported的本质原因是:
Spring MVC 默认注册的所有消息转换器、参数解析器,都没有将application/octet-stream纳入可处理的媒体类型列表:
- 不带
@RequestBody的/test接口默认走表单参数解析逻辑,碰到非表单格式的二进制流请求时,直接触发媒体类型校验报错; - 带
@RequestBody的/test2接口依赖Jackson转换器解析JSON,而Jackson默认只认application/json类媒体类型,碰到application/octet-stream时找不到匹配的处理器,同样会抛异常。
由于是第三方回调无法修改请求头,不需要改动第三方侧,只要在Controller侧做兼容性调整即可,下面是两种常用的落地方式:
方案1:针对性扩展消息转换器的支持范围(最省改动)
这种方式适合确定第三方发的application/octet-stream本质是JSON/纯文本内容的场景,只要给对应的消息转换器追加对该媒体类型的支持,就能让原有代码逻辑不变,正常接参。
- 新增全局WebMvc配置类,给JSON序列化转换器、字符串转换器追加对
application/octet-stream的支持:
@Configuration public class CustomWebMvcConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { for (HttpMessageConverter<?> converter : converters) { // 让Jackson JSON转换器可以处理二进制流格式的JSON请求 if (converter instanceof MappingJackson2HttpMessageConverter) { ((MappingJackson2HttpMessageConverter) converter) .addSupportedMediaTypes(MediaType.APPLICATION_OCTET_STREAM); } // 让字符串转换器可以处理二进制流格式的纯文本请求 if (converter instanceof StringHttpMessageConverter) { ((StringHttpMessageConverter) converter) .addSupportedMediaTypes(MediaType.APPLICATION_OCTET_STREAM); } } } }
- 给对应接口的映射注解放开媒体类型限制,声明接口可以接收任意媒体类型的请求:
// 接纯字符串的接口 @PostMapping(value = "/test", consumes = MediaType.ALL_VALUE) public void notifyTranscodeResult(String paramStr){ ... } // 接实体对象的接口 @PostMapping(value = "/test2", consumes = MediaType.ALL_VALUE) public void notifyTranscodeResult(@RequestBody ParamClass param){ ... }
调整完成后,原有方法体里的逻辑完全不用改,Jackson/字符串转换器会自动把二进制流的内容解析成你需要的参数类型。
方案2:手动读取请求流(最稳妥,不受类型限制)
如果不确定第三方发的内容格式,或者不想调整全局消息转换器配置,可以直接在方法里注入HttpServletRequest,手动读取请求体内容自行解析,这种方式完全绕过Spring的自动参数解析和媒体类型校验,从根源上避免触发异常。
@PostMapping("/test2") public void notifyTranscodeResult(HttpServletRequest request) throws IOException { // 按UTF-8编码读取全部请求内容,避免乱码 String rawContent = new String( request.getInputStream().readAllBytes(), StandardCharsets.UTF_8 ); // 自行解析内容为需要的对象 ObjectMapper objectMapper = new ObjectMapper(); ParamClass param = objectMapper.readValue(rawContent, ParamClass.class); // 原有业务逻辑... }
这种方式不需要加任何特殊注解,也不需要调整全局配置,不管第三方传什么Content-Type,只要能正常读请求流就能拿到参数。
注意事项
- 不要全局粗暴给所有消息转换器放开所有媒体类型的支持,尽量按需追加,避免影响其他接口原有的参数解析逻辑。
- 手动读取请求流时一定要指定明确的字符编码,默认很多容器是ISO-8859-1编码,容易出现中文乱码。
- 如果回调请求是带签名校验的,不要在过滤器或者拦截器里提前读请求流,避免后面Controller里读不到内容,可以用
ContentCachingRequestWrapper包装请求来缓存流内容。
内容的提问来源于stack exchange,提问作者lily
相关产品推荐
相关产品推荐

