最近使用SpringMvc写一个URL比较复杂的RequestMapping,最后使用MockMvc测试下URL。
出现的问题是根本不存在的URI,居然返回200。使用以下代码测试:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration({
"classpath:spring/spring-web.xml"
})
public class SearchControllerTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setUp() throws Exception {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testURLMapping() throws Exception {
String[] testCase= {
"/asdf" // 根本不存在
};
for (String url : testCase) {
this.mockMvc.perform(MockMvcRequestBuilders.get(url))
.andDo(MockMvcResultHandlers.print()) // 输出信息
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
}
测试通过,但是项目跑起来浏览器看HTTP请求返回的是404,输出的信息如下:
MockHttpServletRequest:
HTTP Method = GET
Request URI = /asdf
Parameters = {}
Headers = {}
Handler:
Type = org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 200 // 200!
Error message = null
Headers = {}
Content type = null
Body =
Forwarded URL = default // 问题的关键!
Redirected URL = null
Cookies = []
问题的关键就是Forward那里,这个问题三年前有人反馈过了(https://jira.spring.io/browse/SPR-10695)。
具体原因:
内部进行URL匹配未找到会重定向到default setvlet再返回404错误,这个重定向是在Tomcat内部完成的,客户端是察觉不到的。SpringMvc作为一个servlet可以看责任链模式上的一棒,这个不匹配的请求其实和CSS、Js等静态文件一样最后传递给了Tomcat的default,最终是Tomcat抛出的404错误。MockMvc毕竟不是模拟浏览器访问,MockMvc并不能发现404错误,以上是我个人的理解,推荐看一下上面的链接。
改进办法:
MockMvc并不知道不匹配的请求最后Tomcat怎样处理的,但是可以知道这个请求是在Spring内完成的还是最后做了重定向。修改成如下测试代码:
this.mockMvc.perform(MockMvcRequestBuilders.get(url))
.andDo(MockMvcResultHandlers.print())
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(forwardedUrl(null));
完成!