想必经常在使用springmvc过程中,在接口路径中添加一些参数
但是当参数值包含了文件的后缀名时,例如xxx.mp4,xxx.jpg等,在springboot2.0版本中使用正常,但是在springboot1.5中则会抛出 Could not find acceptable representation 异常,没有找到正确的响应类型.
2.异常原因分析无奈只好一步步断点进入查找原因,最终发现异常原因是springmvc会自动根据URL最后的一段参数解析响应媒体类型,解析出的响应类型与实际的响应类型不匹配,最后没有适合的MediaType响应类型则抛出异常,先给大家查下结果
最后解析为video/mp4类型
部分源码分析如下:
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver implements HandlerMethodReturnValueHandler { protectedvoid writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { HttpServletRequest request = inputMessage.getServletRequest(); //获取适合的响应MediaType List requestedMediaTypes = getAcceptableMediaTypes(request); List producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType); if (outputValue != null && producibleMediaTypes.isEmpty()) { throw new IllegalArgumentException("No converter found for return value of type: " + valueType); } Set compatibleMediaTypes = new linkedHashSet (); for (MediaType requestedType : requestedMediaTypes) { for (MediaType producibleType : producibleMediaTypes) { //MediaType进行匹配 if (requestedType.isCompatibleWith(producibleType)) { compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); } } } //此处没有匹配到合适的MediaType则抛出异常 if (compatibleMediaTypes.isEmpty()) { if (outputValue != null) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } return; } } }
关键方法是获取适合的MediaType,getAcceptableMediaTypes,源码比较简单,关键代码如下:
public ListresolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException { for (ContentNegotiationStrategy strategy : this.strategies) { List mediaTypes = strategy.resolveMediaTypes(request); if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) { continue; } return mediaTypes; } return Collections.emptyList(); }
strategies 是解析策略,项目启动时会初始化,而解析URL后缀名来获取mediaType的策略是 ServletPathExtensionContentNegotiationStrategy类,而这个是啥时候初始化进去的呢,分析发现是在创建ContentNegotiationManagerFactoryBean类时初始化的.
ContentNegotiationManagerFactoryBean类继承了FactoryBean并实现来 InitializingBean,获取bean对象时完成初始化,关键源码如下:
@Override public void afterPropertiesSet() { Liststrategies = new ArrayList (); //favorPathExtension为true则添加 if (this.favorPathExtension) { PathExtensionContentNegotiationStrategy strategy; if (this.servletContext != null && !isUseJafTurnedOff()) { //添加解析URI后缀名策略 strategy = new ServletPathExtensionContentNegotiationStrategy( this.servletContext, this.mediaTypes); } else { strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes); } strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions); if (this.useJaf != null) { strategy.setUseJaf(this.useJaf); } strategies.add(strategy); } this.contentNegotiationManager = new ContentNegotiationManager(strategies); }
favorPathExtension默认值为true
springboot1.5默认添加了ServletPathExtensionContentNegotiationStrategy 进行解析.而在springboot2.0中此值被org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#configureContentNegotiation(springboot1.5和springboot2,0实现不一样,这里就不再深入研究了) 设置为了false
解析到这里,异常原因大概已经清楚了,这里只对比了springboot1.5.4和springboot2.2.2版本.
3.解决方法 3.1 方式一从上面分析中发现,URI解析的方法是 UriUtils.extractFileExtension(path),故可以在URI最后中多添加 '/' ,则可以不让这方法获取到后缀名.例如 http://127.0.0.1:8080/videoMerge/xxxxxx.mp4/
这种方式虽然可以解决,但是请求路径中多添加 / 感觉还是特别别扭.
3.2 方式二从上面分析可以发现,Springboot2.0将favorPathExtension设置为了false不添加ServletPathExtensionContentNegotiationStrategy 策略,故我们也可以添加全局配置,不根据URI后缀名解析响应MediaType
@Configuration public class ContentNegotiationPathConfig extends WebMvcConfigurerAdapter { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { //不解析接口URL后缀名来解析mediaTye configurer.favorPathExtension(false); } }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)