Springboot1.5与Springboot2.0中WebMvcAutoConfiguration不同实现导致Could not find acceptable

Springboot1.5与Springboot2.0中WebMvcAutoConfiguration不同实现导致Could not find acceptable,第1张

Springboot1.5与Springboot2.0中WebMvcAutoConfiguration不同实现导致Could not find acceptable 1.问题现象

想必经常在使用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 {
          protected  void 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 List resolveMediaTypes(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() {
   List strategies = 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);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5596649.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存