spring mvc 406

因为 htm后缀 对应 text/html,所以如果请求是 xxx.htm,不管第二步返回什么,服务端最多只能生成 html 页面。而使用test.xxx,并不在支持的扩展参数里面,所以没有影响。

  • HeaderContentNegotiationStrategy请求头策略,根据 http 的请求头来生成请求可接受的 MedieType。

第二步是获取到服务端支持的可响应的 MedieType,它的规则如下:

  • 获取@RequestMapping 注解的 produces() 标注。
  • 遍历所有的 HttpMessageConverter 获取支持 @RequestMapping 返回值的 MedieType

因为是 URI 扩展参数惹的祸,所以我首先想到的解决方案就是移除 ServletPathExtensionContentNegotiationStrategy 这个策略。

因为是 Spring IOC 来创建对象,所以我想根据 Spring IOC 容器扩展 来解决这个问题。

方法一 : 使用BeanPostProcessor修改 Bean

因为是WebMvcConfigurationSupport#requestMappingHandlerAdapter来创建 RequestMappingHandlerAdapter并且WebMvcConfigurationSupport#mvcContentNegotiationManager创建的 ContentNegotiationManager。所以从容器中获取到 bean Id 为requestMappingHandlerAdapter的 Bean 对象RequestMappingHandlerAdapter,获取到 ContentNegotiationManager。获取直接根据 mvcContentNegotiationManager获取到 ContentNegotiationManager。 然后通过移除 ContentNegotiationManager.strategies策略列表中的 URI 扩展参数策略就可以了。

因为 RequestMappingHandlerAdapter 对象里面没有 ContentNegotiationManager 的获取方法 且 ContentNegotiationManager 类中没有 策略列表的操作方法,所以这个方法不可行。

方法二: 使用BeanFactoryPostProcessor修改 Bean

可以通过 BeanFactoryPostProcessor#postProcessBeanFactory 来修改 BeanDefinition 的属性来移除 策略列表中的 URI 扩展参数策略。

因为 @Configuration 与 @Bean 生成的 BeanDefinition 是把这个 BeanDefinition 伪装成一个 Spring Factory Bean。创建实例直接调用这个方法,而不能通过 BeanDefinition 里面的参数来控制对象的创建。所以这个方法也不可行。

方法三:@EnableMvcConfig

在 WebMvcConfigurationSupport 类中调用 mvcContentNegotiationManager方法生成 ContentNegotiationManager 对象的时候,最终会调用 ContentNegotiationManagerFactoryBean的afterPropertiesSet() 而 favorPathExtension 参数可以控制是否添加 PathExtensionContentNegotiationStrategy,如果这个值为 true 就会添加,反之而不会。这个值的默认值是 true,那么我们可以不可修改这个参数的值呢?

答案是有的,因为在调用ContentNegotiationManagerFactoryBean#afterPropertiesSet方法之前,会调用 WebMvcConfigurationSupport#configureContentNegotiation而我们可以通过继承 WebMvcConfigurerAdapter 类使用 @EnableWebMvc 注解来修改这个值。

下面就是我的测试代码工程结构:

spring mvc 406

> Bootstrap.java

@SpringBootApplicationpublic class Bootstrap { public static void main(String[] args) { SpringApplication.run(Bootstrap.class, args); }}

> MyMvcConfig.java

@Configuration@EnableWebMvcpublic class MyMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.favorPathExtension(false); super.configureContentNegotiation(configurer); }}

>TestController.java

@Controllerpublic class TestController { @RequestMapping("URI地址") @ResponseBody public User user(){ User user = new User(); user.setId("1"); user.setName("carl"); return user; }}

然后再使用以上的请求 URI 做个实验:

请求URI 返回

test 成功返回JSON

test.htm 成功返回JSON

test.xxx 成功返回JSON

并且无论访问哪个 URI 生成的 requestedMediaTypes 都为:

spring mvc 406

并且 http 的请求头如下:

spring mvc 406

spring mvc 406


分享到:


相關文章: