为什么spring只使用一个servlet 使用spring来处理所有请求

Spring MVC 遇到的一点点问题 -
- ITeye技术网站
&& 今天下午下班之前看了看凯歌给的Spring Training的教程的lab篇,我之前有跟着做没有遇到什么问题,但是到了跟Spring MVC integrating的时候,遇到一点点有趣的事情。 这个例子很简单,我照着网上的demo做,然后遇到了点问题,请看下面:项目层次 很简单&
然后是web.xml的配置
&servlet-name&SpringMvcDemo&/servlet-name&
&servlet-class&org.springframework.web.servlet.DispatcherServlet&/servlet-class&
&init-param&
&param-name&contextConfigLocation&/param-name&
&param-value&
/WEB-INF/servlet-config.xml
&/param-value&
&/init-param&
&load-on-startup&1&/load-on-startup&
&/servlet&
&servlet-mapping&
&servlet-name&SpringMvcDemo&/servlet-name&
&url-pattern&/*&/url-pattern&
&/servlet-mapping&
你可以看到这很简单,定义了上下文以及FrontController拦截的请求地址,不过需要注意的我这里的是匹配所有URL请求,包括**.jsp和什么静态的**.png等等--这一点非常重要。
&& 然后是servlet-config.xml 那就跟简单了,主要是一个包扫描,因为我这里的controller都是使用annotation,然后是一个render jsp的view resolver。
&context:component-scan base-package="ht.clark.web" /&
&!-- View Resolver
&bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"&
&property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/&
&property name="prefix" value="/WEB-INF/jsp/"/&
&property name="suffix" value=".jsp"&&/property&
最后便是位于ht.clark.web包下的HomeController:
@Controller
public class HomeController {
@RequestMapping(value="/hi.html",method=RequestMethod.GET)
public ModelAndView Greeting(){
ModelAndView model = new ModelAndView("greeting");
model.addObject("msg" , "Hello to Andy Clark");
}
需要注意的是@RequestMapping配置触发这Greeting方法的来源url,也就是浏览器输入http://localhost:8080/SpringMvcDemo/hi.html将会由dispatcher分配到这方法来处理请求。
那显示结果的JSP页面也很简单--注意使用了Jstl来解析
&%@ taglib prefix="c" uri="/jsp/jstl/core" %&
&h1&This is my message: ${msg}&/h1&
准备工作完毕,这里的流程那应该是非常之简单的,首先所有在浏览器中输入的url都会传递到DispatchServlet这里简单的servlet中(因为我们使用的是/*--匹配所有url输入),然后DispatchServlet扫描并过滤出所有标记为@Controller的类以及其中的@RequestMapping的方法,并且找到匹配value="/hi.html"的,也就是Greeting方法并做一些处理,返回一个ModelAndView对象,然后根据DispatchServlet会在Render这个对象时delegate到你在Servlet-config中配置的ViewResolver的具体实现,比如我们这里的InternalResourceViewResolver,它可以render jsp的显示技术。最后会将view name(也就是greeting)找到位于WEB-INF/jsp下的greeting.jsp并显示出来。
&&&& So,quite straightforward, let's rock and roll!
Oooops,遇到了一点问题,那就是
[color=red]
0:55:30 org.springframework.web.servlet.DispatcherServlet noHandlerFound
警告: No mapping for [/SpringMvcDemo/WEB-INF/jsp/greeting.jsp] in DispatcherServlet with name 'SpringMvcDemo'[/color]
& Why???!!!!& 我的第一反应是,首先我的这个greeting.jsp肯定是在jsp下的,no mapping 是请求没找到,然后去上网搜这个错误嘛,肯定也有人遇到,于是来到了最熟悉的StackOverflow,一搜果然有人遇到这种问题,第一链接 No mapping found for HTTP request with URI [/WEB-INF/pages/apiForm.jsp] 里面提供的解决方案是将 &servlet-mapping&
&servlet-name&SpringMvcDemo&/servlet-name&
&url-pattern&/*&/url-pattern&
&/servlet-mapping&
改为 &servlet-mapping&
&servlet-name&SpringMvcDemo&/servlet-name&
&url-pattern&*.html&/url-pattern&
&/servlet-mapping& --也就是将match everything该为match仅以html结尾的url请求。
& And it works !!
为什么了? 我看后面的回答:
引用When you set the url-pattern to /* then all requests will be sent to that DispatcherServlet, which includes the request for JSP rendering. Though it's not true, it's sometimes useful to think of the InternalResourceView (and derived like JstlView) as another HTTP request, since that way you'll see why a request for the JSP is getting picked up by the DispatcherServlet. – ptomli Aug 12 '09 at 15:16
when i set to / , i the request is render fine. only when i set /* . what is the different / and /* ? – cometta Aug 12 '09 at 15:27
&url-pattern&/&/url-pattern& only matches the URL host/servlet &url-pattern&/*&/url-pattern& matches everything under host/servlet, such as /index.html, /foo.jpg and, most importantly in this case, /WEB-INF/pages/apiForm.jsp the * is the wildcard, which says "anything" In the earlier suggestion, *.do matches anything that ends in .do, for example, /foo.do, /foo/bar.do. It doesn't match anything ending in jsp, so a request for /WEB-INF/pages/apiFrom.jsp is not matched, and is not routed to the DispatcherServlet – ptomli Aug 12 '09 at 15:42
我看ptomli回答的很靠谱。 我查了一下为什么会这样了,下班前凯歌让我去看看源码,我看了下当然是主要的扫了几眼,(因为很多东西搞不清楚),结合Api文档,还好大概的流程有了个概念。
&& 简要地说,当DispatchServlet接受到需要Render的时候,它会去根据需要Render的技术,比如jsp还是xml还是pdf还是velocity等等,在通过你配置的ViewResolver来选择合适的进行处理,所以这里时候它会将请求委托处理resolution InternalResourceViewResoler以及它的子类JstlViewResolver来处理jsp显示的ViewResolver,其实JstlViewResolver或者是InternalResourceViewResoler而言,它们也只是简单调用了RequestDispatch的forward或者include方法来render jsp页面,但是这forward请求,也就是[/SpringMvcDemo/WEB-INF/jsp/greeting.jsp],正如前面所言它匹配DispatchServlet的url表达式(/*),所以它还会交给DispatchServlet来处理,这个时候它肯定是根据你在配置文件中配置或者Controller中annotation比如requestmapping,如果这个时候Controller有一个方法的RequestMapping(value="/WEB-INF/jsp/greeting.jsp')那就会交给它来处理请求,所以,当我们改为使用*.html时候,[/SpringMvcDemo/WEB-INF/jsp/greeting.jsp]并不匹配*.html的格式,所以它不会进入DispatchServlet,而这正是我们想要的,它也不应该route到DispatchServlet中。
&& 所以,这个整个解决问题的过程,还是杰哥说的对,尽管没有说完完整整的自己解决,但是看到别人的解决方案,再想想为什么,然后自己去琢磨,不自觉地有加深对整个框架的理解。
&
浏览: 16559 次
来自: 岳阳
羡慕ing。
能和大牛一起pair,花一倍的时间能得到别人好几 ...
大牛啊膜拜人气:2628242
访问用户量:3443
笔记经验:3830
总积分:261644
级别:VIP5
搜索本笔记
ta的交流分类
ta的全部笔记
浏览(14401)|(0)
&&交流分类:|笔记分类:
随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的。从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用项目(合并到spring3.2中了)。
Spring MVC测试框架提供了对服务器端和客户端(基于RestTemplate的客户端)提供了支持。
对于服务器端:在Spring 3.2之前,我们测试时一般都是直接new控制器,注入依赖,然后判断返回值。但是我们无法连同Spring MVC的基础设施(如DispatcherServlet调度、类型转换、数据绑定、拦截器等)一起测试,另外也没有现成的方法测试如最终渲染的视图(@ResponseBody生成的JSON/XML、JSP、Velocity等)内容是否正确。从Spring 3.2开始这些事情都可以完成了。而且可以测试完整的Spring MVC流程,即从URL请求到控制器处理,再到视图渲染都可以测试。
对于客户端:不需要启动服务器即可测试我们的RESTful 服务。
1 服务器端测试
我的环境:JDK7、Maven3、spring4、Servlet3
首先添加依赖
如下是spring-context和spring-webmvc依赖:
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-context&/artifactId&
&version&${spring.version}&/version&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-webmvc&/artifactId&
&version&${spring.version}&/version&
&/dependency&
版本信息:&spring.version&4.0.0.RELEASE&/spring.version&
如下是测试相关的依赖(junit、hamcrest、mockito、spring-test):
&dependency&
&groupId&junit&/groupId&
&artifactId&junit&/artifactId&
&version&${junit.version}&/version&
&scope&test&/scope&
&/dependency&
&dependency&
&groupId&org.hamcrest&/groupId&
&artifactId&hamcrest-core&/artifactId&
&version&${hamcrest.core.version}/version&
&scope&test&/scope&
&/dependency&
&dependency&
&groupId&org.mockito&/groupId&
&artifactId&mockito-core&/artifactId&
&version&${mockito.core.version}&/version&
&scope&test&/scope&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-test&/artifactId&
&version&${spring.version}&/version&
&scope&test&/scope&
&/dependency&
版本信息:&junit.version&4.11&/junit.version&、&hamcrest.core.version&1.3&/hamcrest.core.version&、&mockito.core.version&1.9.5&/mockito.core.version&
然后准备测试相关配置
package com.sishuok.mvc.
import java.io.S
public class User implements Serializable {
//省略getter/setter等
package com.sishuok.mvc.
//省略import
@Controller
@RequestMapping(&/user&)
public class UserController {
@RequestMapping(&/{id}&)
public ModelAndView view(@PathVariable(&id&) Long id, HttpServletRequest req) {
User user = new User();
user.setId(id);
user.setName(&zhang&);
ModelAndView mv = new ModelAndView();
mv.addObject(&user&, user);
mv.setViewName(&user/view&);
XML风格配置:
:加载非web层组件&
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance&
xmlns:context=&http://www.springframework.org/schema/context&
xsi:schemaLocation=&
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
&!-- 通过web.xml中的 org.springframework.web.context.ContextLoaderListener 加载的
&!-- 请参考 /blog/1602617
&context:component-scan base-package=&com.sishuok.mvc&&
&context:exclude-filter type=&annotation& expression=&org.springframework.stereotype.Controller&/&
&/context:component-scan&
:加载和配置web层组件&
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance&
xmlns:context=&http://www.springframework.org/schema/context&
xmlns:mvc=&http://www.springframework.org/schema/mvc&
xsi:schemaLocation=&
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
&!-- 通过web.xml中的 org.springframework.web.servlet.DispatcherServlet 加载的
&!-- 请参考 /blog/1602617
&context:component-scan base-package=&com.sishuok.mvc& use-default-filters=&false&&
&context:include-filter type=&annotation& expression=&org.springframework.stereotype.Controller&/&
&/context:component-scan&
&mvc:annotation-driven/&
&bean id=&viewResolver& class=&org.springframework.web.servlet.view.InternalResourceViewResolver&&
&property name=&prefix& value=&/WEB-INF/jsp/&/&
&property name=&suffix& value=&.jsp&/&
:此处就不贴了,请前往github查看。
对于context:component-scan注意事项请参考《》和《》。
等价的注解风格配置:&
:等价于spring-config.xml
package com.sishuok.
import org.springframework.ponentS
import org.springframework.context.annotation.C
import org.springframework.context.annotation.FilterT
import org.springframework.stereotype.C
@Configuration
@ComponentScan(basePackages = &com.sishuok.mvc&, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})
public class AppConfig {
:等价于spring-mvc.xml
package com.sishuok.
import org.springframework.context.annotation.B
import org.springframework.ponentS
import org.springframework.context.annotation.C
import org.springframework.context.annotation.FilterT
import org.springframework.stereotype.C
import org.springframework.web.servlet.ViewR
import org.springframework.web.servlet.config.annotation.EnableWebM
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationS
import org.springframework.web.servlet.view.InternalResourceViewR
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = &com.sishuok.mvc&, useDefaultFilters = false, includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})
public class MvcConfig extends WebMvcConfigurationSupport {
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix(&/WEB-INF/jsp/&);
viewResolver.setSuffix(&.jsp&);
return viewR
:注册相应的web.xml中的组件
package com.sishuok.
import org.springframework.web.WebApplicationI
import org.springframework.web.context.ContextLoaderL
import org.springframework.web.context.support.AnnotationConfigWebApplicationC
import org.springframework.web.filter.CharacterEncodingF
import org.springframework.web.servlet.DispatcherS
import javax.servlet.DispatcherT
import javax.servlet.FilterR
import javax.servlet.ServletE
import javax.servlet.ServletR
import java.util.EnumS
public class WebInitializer implements WebApplicationInitializer {
public void onStartup(javax.servlet.ServletContext sc) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
sc.addListener(new ContextLoaderListener(rootContext));
//2、springmvc上下文
AnnotationConfigWebApplicationContext springMvcContext = new AnnotationConfigWebApplicationContext();
springMvcContext.register(MvcConfig.class);
//3、DispatcherServlet
DispatcherServlet dispatcherServlet = new DispatcherServlet(springMvcContext);
ServletRegistration.Dynamic dynamic = sc.addServlet(&dispatcherServlet&, dispatcherServlet);
dynamic.setLoadOnStartup(1);
dynamic.addMapping(&/&);
//4、CharacterEncodingFilter
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding(&utf-8&);
FilterRegistration filterRegistration =
sc.addFilter(&characterEncodingFilter&, characterEncodingFilter);
filterRegistration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, &/&);
对于WebInitializer,请参考《》
到此基本的配置就搞定了,接下来看看如何测试吧。&
1.1 以前的测试方式
package com.sishuok.mvc.
//省略import
public class UserControllerTest {
private UserController userC
public void setUp() {
userController = new UserController();
//安装userCtroller依赖 比如userService
public void testView() {
MockHttpServletRequest req = new MockHttpServletRequest();
ModelAndView mv = userController.view(1L, req);
ModelAndViewAssert.assertViewName(mv, &user/view&);
ModelAndViewAssert.assertModelAttributeAvailable(mv, &user&);
准备控制器:我们通过new方式创建一个,然后手工查找依赖注入进去(比如从spring容器获取/new的);
Mock Request:此处使用Spring提供的Mock API模拟一个HttpServletRequest,其他的Servlet API也提供了相应的Mock类,具体请查看Javadoc;
访问控制器方法:通过直接调用控制器方法进行访问,此处无法验证Spring MVC框架的类型转换、数据验证等是否正常;
ModelAndViewAssert:通过这个Assert API验证我们的返回值是否正常;
对于单元测试步骤请参考:&
这种方式的缺点已经说过了,如不能走Spring MVC完整流程(不能走Servlet的过滤器链、SpringMVC的类型转换、数据验证、数据绑定、拦截器等等),如果做基本的测试没问题,这种方式就是纯粹的单元测试,我们想要的功能其实是一种集成测试,不过后续部分不区分。
1.2 安装测试环境
spring mvc测试框架提供了两种方式,独立安装和集成Web环境测试(此种方式并不会集成真正的web环境,而是通过相应的Mock API进行模拟测试,无须启动服务器)。
独立测试方式
public class UserControllerStandaloneSetupTest {
private MockMvc mockM
public void setUp() {
UserController userController = new UserController();
mockMvc = MockMvcBuilders.standaloneSetup(userController).build();
1、首先自己创建相应的控制器,注入相应的依赖
2、通过MockMvcBuilders.standaloneSetup模拟一个Mvc测试环境,通过build得到一个MockMvc
3、MockMvc:是我们以后测试时经常使用的API,后边介绍
集成Web环境方式
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration(value = &src/main/webapp&)
@ContextHierarchy({
@ContextConfiguration(name = &parent&, locations = &classpath:spring-config.xml&),
@ContextConfiguration(name = &child&, locations = &classpath:spring-mvc.xml&)
//注解风格
//@RunWith(SpringJUnit4ClassRunner.class)
//@WebAppConfiguration(value = &src/main/webapp&)
//@ContextHierarchy({
@ContextConfiguration(name = &parent&, classes = AppConfig.class),
@ContextConfiguration(name = &child&, classes = MvcConfig.class)
public class UserControllerWebAppContextSetupTest {
@Autowired
private WebApplicationC
private MockMvc mockM
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
1、@WebAppConfiguration:测试环境使用,用来表示测试环境使用的ApplicationContext将是WebApplicationContext类型的;value指定web应用的根;
2、@ContextHierarchy:指定容器层次,即spring-config.xml是父容器,而spring-mvc.xml是子容器,请参考《》
3、通过@Autowired&WebApplicationContext wac:注入web环境的ApplicationContext容器;
4、然后通过MockMvcBuilders.webAppContextSetup(wac).build()创建一个MockMvc进行测试;
到此测试环境就搭建完成了,根据需要选择使用哪种方式即可。相关配置请前往。
1.3、HelloWorld
public void testView() throws Exception {
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(&/user/1&))
.andExpect(MockMvcResultMatchers.view().name(&user/view&))
.andExpect(MockMvcResultMatchers.model().attributeExists(&user&))
.andDo(MockMvcResultHandlers.print())
.andReturn();
Assert.assertNotNull(result.getModelAndView().getModel().get(&user&));
1、mockMvc.perform执行一个请求;
2、MockMvcRequestBuilders.get(&/user/1&)构造一个请求
3、ResultActions.andExpect添加执行完成后的断言
4、ResultActions.andDo添加一个结果处理器,表示要对结果做点什么事情,比如此处使用MockMvcResultHandlers.print()输出整个响应结果信息。
5、ResultActions.andReturn表示执行完成后返回相应的结果。
整个测试过程非常有规律:
1、准备测试环境
2、通过MockMvc执行请求
3.1、添加验证断言
3.2、添加结果处理器
3.3、得到MvcResult进行自定义断言/进行下一步的异步请求
4、卸载测试环境
对于单元测试步骤请参考:。
1.4、了解测试API
Spring mvc测试框架提供了测试MVC需要的API,主要包括Servlet/JSP Mock、MockMvcBuilder、MockMvc、RequestBuilder、ResultMatcher、ResultHandler、MvcResult等。另外提供了几个静态工厂方法便于测试:MockMvcBuilders、MockMvcRequestBuilders、MockMvcResultMatchers、MockMvcResultHandlers。在使用时请使用静态方法导入方便测试,如:
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
Servlet/JSP API Mock&
提供了对Servlet 3 相应API的Mock,如:
MockServletContext
MockHttpServletRequest
MockHttpServletResponse
具体请查看spring-test模块的org.springframework.mock.web包。
MockMvcBuilder/MockMvcBuilders
MockMvcBuilder是用来构造MockMvc的构造器,其主要有两个实现:StandaloneMockMvcBuilder和DefaultMockMvcBuilder,分别对应之前的两种测试方式。对于我们来说直接使用静态工厂MockMvcBuilders创建即可:
MockMvcBuilders.webAppContextSetup(WebApplicationContext context):指定WebApplicationContext,将会从该上下文获取相应的控制器并得到相应的MockMvc;
MockMvcBuilders.standaloneSetup(Object... controllers):通过参数指定一组控制器,这样就不需要从上下文获取了;
其中DefaultMockMvcBuilder还提供了如下API:
addFilters(Filter... filters)/addFilter(Filter filter, String... urlPatterns):添加javax.servlet.Filter过滤器
defaultRequest(RequestBuilder requestBuilder):默认的RequestBuilder,每次执行时会合并到自定义的RequestBuilder中,即提供公共请求数据的;
alwaysExpect(ResultMatcher resultMatcher):定义全局的结果验证器,即每次执行请求时都进行验证的规则;
alwaysDo(ResultHandler resultHandler):定义全局结果处理器,即每次请求时都进行结果处理;
dispatchOptions:DispatcherServlet是否分发OPTIONS请求方法到控制器;
StandaloneMockMvcBuilder继承了DefaultMockMvcBuilder,又提供了如下API:
setMessageConverters(HttpMessageConverter&?&...messageConverters):设置HTTP消息转换器;
setValidator(Validator validator):设置验证器;
setConversionService(FormattingConversionService conversionService):设置转换服务;
addInterceptors(HandlerInterceptor... interceptors)/addMappedInterceptors(String[] pathPatterns, HandlerInterceptor... interceptors):添加spring mvc拦截器;
setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager):设置内容协商管理器;
setAsyncRequestTimeout(long timeout):设置异步超时时间;
setCustomArgumentResolvers(HandlerMethodArgumentResolver... argumentResolvers):设置自定义控制器方法参数解析器;
setCustomReturnValueHandlers(HandlerMethodReturnValueHandler... handlers):设置自定义控制器方法返回值处理器;
setHandlerExceptionResolvers(List&HandlerExceptionResolver& exceptionResolvers)/setHandlerExceptionResolvers(HandlerExceptionResolver... exceptionResolvers):设置异常解析器;
setViewResolvers(ViewResolver...resolvers):设置视图解析器;
setSingleView(View view):设置单个视图,即视图解析时总是解析到这一个(仅适用于只有一个视图的情况);
setLocaleResolver(LocaleResolver localeResolver):设置Local解析器;
setFlashMapManager(FlashMapManager flashMapManager):设置FlashMapManager,如存储重定向数据;
setUseSuffixPatternMatch(boolean useSuffixPatternMatch):设置是否是后缀模式匹配,如“/user”是否匹配&/user.*&,默认真即匹配;
setUseTrailingSlashPatternMatch(boolean useTrailingSlashPatternMatch):设置是否自动后缀路径模式匹配,如“/user”是否匹配“/user/”,默认真即匹配;
addPlaceHolderValue(String name, String value) :添加request mapping中的占位符替代;
因为StandaloneMockMvcBuilder不会加载Spring MVC配置文件,因此就不会注册我们需要的一些组件,因此就提供了如上API用于注册我们需要的相应组件。
使用之前的MockMvcBuilder.build()得到构建好的MockMvc;这个是mvc测试的核心API,对于该API的使用方式如下:
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.get(&/user/1&))
.andExpect(MockMvcResultMatchers.view().name(&user/view&))
.andExpect(MockMvcResultMatchers.model().attributeExists(&user&))
.andDo(MockMvcResultHandlers.print())
.andReturn();
perform:执行一个RequestBuilder请求,会自动执行SpringMVC的流程并映射到相应的控制器执行处理;
andExpect:添加ResultMatcher验证规则,验证控制器执行完成后结果是否正确;
andDo:添加ResultHandler结果处理器,比如调试时打印结果到控制台;
andReturn:最后返回相应的MvcResult;然后进行自定义验证/进行下一步的异步处理;
另外还提供了以下API:
setDefaultRequest:设置默认的RequestBuilder,用于在每次perform执行相应的RequestBuilder时自动把该默认的RequestBuilder合并到perform的RequestBuilder中;
setGlobalResultMatchers:设置全局的预期结果验证规则,如我们通过MockMvc测试多个控制器时,假设它们都想验证某个规则时,就可以使用这个;
setGlobalResultHandlers:设置全局的ResultHandler结果处理器;
RequestBuilder/MockMvcRequestBuilders
从名字可以看出,RequestBuilder用来构建请求的,其提供了一个方法buildRequest(ServletContext servletContext)用于构建MockHttpServletRequest;其主要有两个子类MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder(如文件上传使用),即用来Mock客户端请求需要的所有数据。
MockMvcRequestBuilders主要API:
MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables):根据uri模板和uri变量值得到一个GET请求方式的MockHttpServletRequestBuilder;如get(&/user/{id}&, 1L);
MockHttpServletRequestBuilder post(String urlTemplate, Object... urlVariables):同get类似,但是是POST方法;
MockHttpServletRequestBuilder put(String urlTemplate, Object... urlVariables):同get类似,但是是PUT方法;
MockHttpServletRequestBuilder delete(String urlTemplate, Object... urlVariables) :同get类似,但是是DELETE方法;
MockHttpServletRequestBuilder options(String urlTemplate, Object... urlVariables):同get类似,但是是OPTIONS方法;
MockHttpServletRequestBuilder request(HttpMethod httpMethod, String urlTemplate, Object... urlVariables):提供自己的Http请求方法及uri模板和uri变量,如上API都是委托给这个API;
MockMultipartHttpServletRequestBuilder fileUpload(String urlTemplate, Object... urlVariables):提供文件上传方式的请求,得到MockMultipartHttpServletRequestBuilder;
RequestBuilder asyncDispatch(final MvcResult mvcResult):创建一个从启动异步处理的请求的MvcResult进行异步分派的RequestBuilder;
接下来再看看MockHttpServletRequestBuilder和MockMultipartHttpServletRequestBuilder API:
MockHttpServletRequestBuilder API:
MockHttpServletRequestBuilder header(String name, Object... values)/MockHttpServletRequestBuilder headers(HttpHeaders httpHeaders):添加头信息;
MockHttpServletRequestBuilder contentType(MediaType mediaType):指定请求的contentType头信息;
MockHttpServletRequestBuilder accept(MediaType... mediaTypes)/MockHttpServletRequestBuilder accept(String... mediaTypes):指定请求的Accept头信息;
MockHttpServletRequestBuilder content(byte[] content)/MockHttpServletRequestBuilder content(String content):指定请求Body体内容;
MockHttpServletRequestBuilder cookie(Cookie... cookies):指定请求的Cookie;
MockHttpServletRequestBuilder locale(Locale locale):指定请求的Locale;
MockHttpServletRequestBuilder characterEncoding(String encoding):指定请求字符编码;
MockHttpServletRequestBuilder requestAttr(String name, Object value) :设置请求属性数据;
MockHttpServletRequestBuilder sessionAttr(String name, Object value)/MockHttpServletRequestBuilder sessionAttrs(Map&String, Object& sessionAttributes):设置请求session属性数据;
MockHttpServletRequestBuilder flashAttr(String name, Object value)/MockHttpServletRequestBuilder flashAttrs(Map&String, Object& flashAttributes):指定请求的flash信息,比如重定向后的属性信息;
MockHttpServletRequestBuilder session(MockHttpSession session) :指定请求的Session;
MockHttpServletRequestBuilder principal(Principal principal) :指定请求的Principal;
MockHttpServletRequestBuilder contextPath(String contextPath) :指定请求的上下文路径,必须以“/”开头,且不能以“/”结尾;
MockHttpServletRequestBuilder pathInfo(String pathInfo) :请求的路径信息,必须以“/”开头;
MockHttpServletRequestBuilder secure(boolean secure):请求是否使用安全通道;
MockHttpServletRequestBuilder with(RequestPostProcessor postProcessor):请求的后处理器,用于自定义一些请求处理的扩展点;
MockMultipartHttpServletRequestBuilder继承自MockHttpServletRequestBuilder,又提供了如下API:
MockMultipartHttpServletRequestBuilder file(String name, byte[] content)/MockMultipartHttpServletRequestBuilder file(MockMultipartFile file):指定要上传的文件;
ResultActions
调用MockMvc.perform(RequestBuilder requestBuilder)后将得到ResultActions,通过ResultActions完成如下三件事:
ResultActions andExpect(ResultMatcher matcher) :添加验证断言来判断执行请求后的结果是否是预期的;
ResultActions andDo(ResultHandler handler) :添加结果处理器,用于对验证成功后执行的动作,如输出下请求/结果信息用于调试;
MvcResult andReturn() :返回验证成功后的MvcResult;用于自定义验证/下一步的异步处理;
ResultMatcher/MockMvcResultMatchers
ResultMatcher用来匹配执行完请求后的结果验证,其就一个match(MvcResult result)断言方法,如果匹配失败将抛出相应的异常;spring mvc测试框架提供了很多***ResultMatchers来满足测试需求。注意这些***ResultMatchers并不是ResultMatcher的子类,而是返回ResultMatcher实例的。Spring mvc测试框架为了测试方便提供了MockMvcResultMatchers静态工厂方法方便操作;具体的API如下:
HandlerResultMatchers handler():请求的Handler验证器,比如验证处理器类型/方法名;此处的Handler其实就是处理请求的控制器;
RequestResultMatchers request():得到RequestResultMatchers验证器;
ModelResultMatchers model():得到模型验证器;
ViewResultMatchers view():得到视图验证器;
FlashAttributeResultMatchers flash():得到Flash属性验证;
StatusResultMatchers status():得到响应状态验证器;
HeaderResultMatchers header():得到响应Header验证器;
CookieResultMatchers cookie():得到响应Cookie验证器;
ContentResultMatchers content():得到响应内容验证器;
JsonPathResultMatchers jsonPath(String expression, Object ... args)/ResultMatcher jsonPath(String expression, Matcher&T& matcher):得到Json表达式验证器;
XpathResultMatchers xpath(String expression, Object... args)/XpathResultMatchers xpath(String expression, Map&String, String& namespaces, Object... args):得到Xpath表达式验证器;
ResultMatcher forwardedUrl(final String expectedUrl):验证处理完请求后转发的url(绝对匹配);
ResultMatcher forwardedUrlPattern(final String urlPattern):验证处理完请求后转发的url(Ant风格模式匹配,@since spring4);
ResultMatcher redirectedUrl(final String expectedUrl):验证处理完请求后重定向的url(绝对匹配);
ResultMatcher redirectedUrlPattern(final String expectedUrl):验证处理完请求后重定向的url(Ant风格模式匹配,@since spring4);
得到相应的***ResultMatchers后,接着再调用其相应的API得到ResultMatcher,如ModelResultMatchers.attributeExists(final String... names)判断Model属性是否存在。具体请查看相应的API。再次就不一一列举了。
ResultHandler/MockMvcResultHandlers
ResultHandler用于对处理的结果进行相应处理的,比如输出整个请求/响应等信息方便调试,Spring mvc测试框架提供了MockMvcResultHandlers静态工厂方法,该工厂提供了ResultHandler print()返回一个输出MvcResult详细信息到控制台的ResultHandler实现。
即执行完控制器后得到的整个结果,并不仅仅是返回值,其包含了测试时需要的所有信息,如:
MockHttpServletRequest getRequest():得到执行的请求;
MockHttpServletResponse getResponse():得到执行后的响应;
Object getHandler():得到执行的处理器,一般就是控制器;
HandlerInterceptor[] getInterceptors():得到对处理器进行拦截的拦截器;
ModelAndView getModelAndView():得到执行后的ModelAndView;
Exception getResolvedException():得到HandlerExceptionResolver解析后的异常;
FlashMap getFlashMap():得到FlashMap;
Object getAsyncResult()/Object getAsyncResult(long timeout):得到异步执行的结果;
1.5 测试示例
测试普通控制器&
//测试普通控制器
mockMvc.perform(get(&/user/{id}&, 1)) //执行请求
.andExpect(model().attributeExists(&user&)) //验证存储模型数据
.andExpect(view().name(&user/view&)) //验证viewName
.andExpect(forwardedUrl(&/WEB-INF/jsp/user/view.jsp&))//验证视图渲染时forward到的jsp
.andExpect(status().isOk())//验证状态码
.andDo(print()); //输出MvcResult到控制台
测试普通控制器,但是URL错误,即404
//找不到控制器,404测试
MvcResult result = mockMvc.perform(get(&/user2/{id}&, 1)) //执行请求
.andDo(print())
.andExpect(status().isNotFound()) //验证控制器不存在
.andReturn();
Assert.assertNull(result.getModelAndView()); //自定义断言
得到MvcResult自定义验证 &&&
MvcResult result = mockMvc.perform(get(&/user/{id}&, 1))//执行请求
.andReturn(); //返回MvcResult
Assert.assertNotNull(result.getModelAndView().getModel().get(&user&)); //自定义断言
验证请求参数绑定到模型数据及Flash属性&
mockMvc.perform(post(&/user&).param(&name&, &zhang&)) //执行传递参数的POST请求(也可以post(&/user?name=zhang&))
.andExpect(handler().handlerType(UserController.class)) //验证执行的控制器类型
.andExpect(handler().methodName(&create&)) //验证执行的控制器方法名
.andExpect(model().hasNoErrors()) //验证页面没有错误
.andExpect(flash().attributeExists(&success&)) //验证存在flash属性
.andExpect(view().name(&redirect:/user&)); //验证视图
验证请求参数验证失败出错&&
mockMvc.perform(post(&/user&).param(&name&, &admin&)) //执行请求
.andExpect(model().hasErrors()) //验证模型有错误
.andExpect(model().attributeDoesNotExist(&name&)) //验证存在错误的属性
.andExpect(view().name(&showCreateForm&)); //验证视图
//文件上传
byte[] bytes = new byte[] {1, 2};
mockMvc.perform(fileUpload(&/user/{id}/icon&, 1L).file(&icon&, bytes)) //执行文件上传
.andExpect(model().attribute(&icon&, bytes)) //验证属性相等性
.andExpect(view().name(&success&)); //验证视图
JSON请求/响应验证
测试时需要安装jackson Json和JsonPath依赖:&
&dependency&
&groupId&com.fasterxml.jackson.core&/groupId&
&artifactId&jackson-databind&/artifactId&
&version&${jackson2.version}&/version&
&/dependency&
&dependency&
&groupId&com.jayway.jsonpath&/groupId&
&artifactId&json-path&/artifactId&
&version&${jsonpath.version}&/version&
&scope&test&/scope&
&/dependency& 版本:&jsonpath.version&0.9.0&/jsonpath.version&、&jackson2.version&2.2.3&/jackson2.version&&
String requestBody = &{\&id\&:1, \&name\&:\&zhang\&}&;
mockMvc.perform(post(&/user&)
.contentType(MediaType.APPLICATION_JSON).content(requestBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(content().contentType(MediaType.APPLICATION_JSON)) //验证响应contentType
.andExpect(jsonPath(&$.id&).value(1)); //使用Json path验证JSON 请参考http://goessner.net/articles/JsonPath/
String errorBody = &{id:1, name:zhang}&;
MvcResult result = mockMvc.perform(post(&/user&)
.contentType(MediaType.APPLICATION_JSON).content(errorBody)
.accept(MediaType.APPLICATION_JSON)) //执行请求
.andExpect(status().isBadRequest()) //400错误请求
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体
XML请求/响应验证
测试时需要安装spring oxm和xstream依赖:&
&dependency&
&groupId&com.thoughtworks.xstream&/groupId&
&artifactId&xstream&/artifactId&
&version&${xsream.version}&/version&
&scope&test&/scope&
&/dependency&
&dependency&
&groupId&org.springframework&/groupId&
&artifactId&spring-oxm&/artifactId&
&version&${spring.version}&/version&
&scope&test&/scope&
&/dependency&
版本:&xstream.version&1.4.4&/xstream.version&
//XML请求/响应
String requestBody = &&user&&id&1&/id&&name&zhang&/name&&/user&&;
mockMvc.perform(post(&/user&)
.contentType(MediaType.APPLICATION_XML).content(requestBody)
.accept(MediaType.APPLICATION_XML)) //执行请求
.andDo(print())
.andExpect(content().contentType(MediaType.APPLICATION_XML)) //验证响应contentType
.andExpect(xpath(&/user/id/text()&).string(&1&)); //使用XPath表达式验证XML 请参考.cn/xpath/
String errorBody = &&user&&id&1&/id&&name&zhang&/name&&;
MvcResult result = mockMvc.perform(post(&/user&)
.contentType(MediaType.APPLICATION_XML).content(errorBody)
.accept(MediaType.APPLICATION_XML)) //执行请求
.andExpect(status().isBadRequest()) //400错误请求
.andReturn();
Assert.assertTrue(HttpMessageNotReadableException.class.isAssignableFrom(result.getResolvedException().getClass()));//错误的请求内容体
异常处理&&
//异常处理
MvcResult result = mockMvc.perform(get(&/user/exception&)) //执行请求
.andExpect(status().isInternalServerError()) //验证服务器内部错误
.andReturn();
Assert.assertTrue(IllegalArgumentException.class.isAssignableFrom(result.getResolvedException().getClass()));
//静态资源
mockMvc.perform(get(&/static/app.js&)) //执行请求
.andExpect(status().isOk()) //验证状态码200
.andExpect(content().string(CoreMatchers.containsString(&var&)));//验证渲染后的视图内容包含var
mockMvc.perform(get(&/static/app1.js&)) //执行请求
.andExpect(status().isNotFound());
//验证状态码404
//Callable
MvcResult result = mockMvc.perform(get(&/user/async1?id=1&name=zhang&)) //执行请求
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class))) //默认会等10秒超时
.andReturn();
mockMvc.perform(asyncDispatch(result))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath(&$.id&).value(1));
//DeferredResult
result = mockMvc.perform(get(&/user/async2?id=1&name=zhang&)) //执行请求
.andExpect(request().asyncStarted())
.andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class)))
//默认会等10秒超时
.andReturn();
mockMvc.perform(asyncDispatch(result))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath(&$.id&).value(1));
此处请在第一次请求时加上 andExpect(request().asyncResult(CoreMatchers.instanceOf(User.class)))这样会等待结果返回/超时,无须自己设置线程等待了;此处注意request().asyncResult一定是在第一次请求发出;然后第二次通过asyncDispatch进行异步请求。
添加自定义过滤器
mockMvc = webAppContextSetup(wac).addFilter(new MyFilter(), &/*&).build();
mockMvc.perform(get(&/user/1&))
.andExpect(request().attribute(&filter&, true));
mockMvc = webAppContextSetup(wac)
.defaultRequest(get(&/user/1&).requestAttr(&default&, true)) //默认请求 如果其是Mergeable类型的,会自动合并的哦mockMvc.perform中的RequestBuilder
.alwaysDo(print())
//默认每次执行请求后都做的动作
.alwaysExpect(request().attribute(&default&, true)) //默认每次执行后进行验证的断言
mockMvc.perform(get(&/user/1&))
.andExpect(model().attributeExists(&user&));
& 以上代码请参考
。更多参考示例请
只要记住测试步骤,按照步骤操作,整个测试过程是非常容易理解的:
1、准备测试环境
2、通过MockMvc执行请求
3.1、添加验证断言
3.2、添加结果处理器
3.3、得到MvcResult进行自定义断言/进行下一步的异步请求
4、卸载测试环境
对于单元测试步骤请参考:。
下一篇介绍RestTemplate客户端测试。
欢迎加入spring群进行交流。
相关笔记推荐
精品视频课程推荐
WebLogic基础知识:WebLogic基本概念、正确安装WebLogic、建域、应用部署于JDBC选择、对WebLogic的监控和日志查看、集群的高可用性;课程目标:彻底掌握WebLogic的基本概念,在理解基本概念的基础上做到正确的安装WebLogic,根据不同的需求创建域,合理选择应用部署和JDBC配置。熟练掌握WebLogic的console监控,了解各种性能和运行指标,以及对监控结果的分析,运用集群的高可用性,对集群架设。
本视频课程是北京Java私塾原创精品书籍《研磨设计模式》一书的配套学习视频,由《研磨设计模式》的第一作者CC录制
课程目标:全面、系统的掌握GoF设计模式的知识,达到可以在实际项目开发中运用的能力
技术要点:如何实现可配置、如何实现缓存以及缓存的管理、如何实现用缓存来控制多实例的创建、如何实现参数化工厂、 如何实现可扩展工厂、如何实现原型管理器、如何实现Java的静态代理和动态代理、如何实现多线程处理队列请求、 如何实现命令的参数化配置、可撤销的操作、宏命令、队列请求和日志请求、如何实现翻页迭代、如何检测环状结构、 如何实现通用的增删改查、如何模拟工作流来处理流程、如何实现简单又通用的XML读取、如何实现模拟AOP的功能......
系统、完整的学习Spring Web MVC开发的知识。包括:Spring Web MVC入门;理解DispatcherServlet;注解式控制器开发详解;数据类型转换;数据格式化;数据验证; 拦截器;对Ajax的支持;文件上传下载;表单标签等内容;最后以一个综合的CRUD带翻页的应用示例来综合所学的知识
系统、完整的学习Spring Data JPA开发的知识。包括:Spring Data JPA入门;JpaRepository基本功能 ;JpaRepository的查询;客户化扩展JpaRepository;Specifications查询。
达到能综合使用Struts2+Spring3+Hibernate3+Jbpm4来进行实际项目开发的能力。
包括:ssh和jbpm的整合;数据字典;通用DAO(Spring+Hibernate+泛型+反射+SpEL+模板方法模式);自动生成UUID的加强版;分层开发、SSH联合的基本开发;翻页的示范真实值和表现值,数据参照的实现;文件上传下载;主子表操;登录验证码;登录控制的拦截器
浏览(14401)|(0)
&&交流分类:|笔记分类:
版权所有 Copyright(C) 私塾在线学习网}

我要回帖

更多关于 spring mvc servlet 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信