如何避免使用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(因为前面处理的是相同的)会处理请求,您将进入一个无限循环。
当你用一个特定的prefix
和suffix
声明一个ThymeleafViewResolver
和一个ServletContextTemplateResolver
时,它build立了不同的View
,给它一个像
WEB-INF/web-templates/preference.html
ThymeleafView
实例通过使用ServletContextResourceResolver
find相对于ServletContext
path的文件
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
最终
return servletContext.getResourceAsStream(resourceName);
这得到一个相对于ServletContext
path的资源。 然后可以使用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); } }