如何避免使用Spring MVCtesting的“循环视图path”exception

我在我的一个控制器中有以下代码:

@Controller @RequestMapping("/preference") public class PreferenceController { @RequestMapping(method = RequestMethod.GET, produces = "text/html") public String preference() { return "preference"; } 

我只是试图使用Spring MVCtesting来testing它,如下所示:

 @ContextConfiguration @WebAppConfiguration @RunWith(SpringJUnit4ClassRunner.class) public class PreferenceControllerTest { @Autowired private WebApplicationContext ctx; private MockMvc mockMvc; @Before public void setup() { mockMvc = webAppContextSetup(ctx).build(); } @Test public void circularViewPathIssue() throws Exception { mockMvc.perform(get("/preference"))// .andDo(print()); } 

我收到以下exception:

圆形视图path[首选项]:将重新发送回当前的处理程序URL [/ preference]。 检查你的ViewResolver设置! (提示:由于默认视图名称的生成,这可能是未指定视图的结果。)

我觉得奇怪的是, 当我加载包含模板和视图parsing器的“完整”上下文configuration时它工作正常 ,如下所示:

 <bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="webTemplateResolver"> <property name="prefix" value="WEB-INF/web-templates/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5" /> <property name="characterEncoding" value="UTF-8" /> <property name="order" value="2" /> <property name="cacheable" value="false" /> </bean> 

我很清楚,由模板parsing器添加的前缀可确保当应用程序使用此模板parsing器时不存在“循环视图path”。

但是,那么我应该如何使用Spring MVCtesting来testing我的应用程序呢? 有没有人有任何线索?

这与Spring MVCtesting无关。

当你不声明一个ViewResolver ,Spring注册一个默认的InternalResourceViewResolver ,它创buildJstlView实例来渲染View

JstlView类扩展了InternalResourceView

用于同一Web应用程序内的JSP或其他资源的封装器。 将模型对象作为请求属性公开,并使用javax.servlet.RequestDispatcher将请求转发到指定的资源URL。

该视图的URL应该指定Web应用程序内的资源,适用于RequestDispatcher的forward或include方法。

大胆是我的。 换句话说,渲染之前的视图将尝试获取要forward()RequestDispatcher forward() 。 在这之前,它会检查以下内容

 if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) { throw new ServletException("Circular view path [" + path + "]: would dispatch back " + "to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " + "(Hint: This may be the result of an unspecified view, due to default view name generation.)"); } 

其中path是视图名称,您从@Controller返回的内容。 在这个例子中,这是preference 。 variablesuri保存正在处理的请求的URI,即/context/preference

上面的代码意识到,如果您要转发到/context/preference ,那么同一个servlet(因为前面处理的是相同的)会处理请求,您将进入一个无限循环。


当你用一个特定的prefixsuffix声明一个ThymeleafViewResolver和一个ServletContextTemplateResolver时,它build立了不同的View ,给它一个像

 WEB-INF/web-templates/preference.html 

ThymeleafView实例通过使用ServletContextResourceResolverfind相对于ServletContextpath的文件

 templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);` 

最终

 return servletContext.getResourceAsStream(resourceName); 

这得到一个相对于ServletContextpath的资源。 然后可以使用TemplateEngine生成HTML。 没有办法可以在这里发生无尽的循环。

我使用如下所示的@ResponseBody解决了这个问题:

 @RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"}) @ResponseStatus(HttpStatus.OK) @Transactional(value = "jpaTransactionManager") public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) { 

这是我解决这个问题的方法:

 @Before public void setup() { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setPrefix("/WEB-INF/jsp/view/"); viewResolver.setSuffix(".jsp"); mockMvc = MockMvcBuilders.standaloneSetup(new HelpController()) .setViewResolvers(viewResolver) .build(); } 

如果你真的不关心渲染视图,这是一个简单的修复。

创build不检查圆形视图path的InternalResourceViewResolver的子类:

 public class StandaloneMvcTestViewResolver extends InternalResourceViewResolver { public StandaloneMvcTestViewResolver() { super(); } @Override protected AbstractUrlBasedView buildView(final String viewName) throws Exception { final InternalResourceView view = (InternalResourceView) super.buildView(viewName); // prevent checking for circular view paths view.setPreventDispatchLoop(false); return view; } } 

然后用它来设置你的testing:

 MockMvc mockMvc; @Before public void setUp() { final MyController controller = new MyController(); mockMvc = MockMvcBuilders.standaloneSetup(controller) .setViewResolvers(new StandaloneMvcTestViewResolver()) .build(); } 

对于Thymeleaf:

我刚开始使用春季4和百里香,当我遇到这个错误时,通过join来解决:

 <bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver"> <property name="templateEngine" ref="templateEngine" /> <property name="order" value="0" /> </bean> 

我使用注释来configurationspring web应用程序,这个问题通过向configuration添加一个InternalResourceViewResolver bean来解决。 希望这会有所帮助。

 @Configuration @EnableWebMvc @ComponentScan(basePackages = { "com.example.springmvc" }) public class WebMvcConfig extends WebMvcConfigurerAdapter { @Bean public InternalResourceViewResolver internalResourceViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/jsp/"); resolver.setSuffix(".jsp"); return resolver; } } 

另一种简单的方法

 package org.yourpackagename; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.web.SpringBootServletInitializer; @SpringBootApplication public class Application extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(PreferenceController.class); } public static void main(String[] args) { SpringApplication.run(PreferenceController.class, args); } }