因为 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 注解来修改这个值。
下面就是我的测试代码工程结构:
> 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 都为:
并且 http 的请求头如下:
閱讀更多 Java程序猿鼓勵師 的文章