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


分享到:


相關文章: