大坑”Circular view path”

如何在Spring MVC Test中避免”Circular view path” 异常

  1. 问题的现象

比如在webConfig中定义了一个viewResolver

复制代码
public class WebConfig extends WebMvcConfigurerAdapter {

//配置JSP视图解析器
@Bean
public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("WEB-INF/views/");
    resolver.setSuffix(".jsp");
    resolver.setExposeContextBeansAsAttributes(true);
    return resolver;
}

}
复制代码
然后定义了一个controller,URL路径为"/home", 它返回名字叫home的view

复制代码
@Controller
public class HomeController {
@RequestMapping(value = “/home”, method=GET)
public ModelAndView home() {
String message = “Hello”;
return new ModelAndView(“home”, “home”, message);
}
}
复制代码
然后定义了个Test

复制代码
public class HomeControllerTest {
@Test
public void testHomePage() throws Exception {
HomeController controller = new HomeController();
MockMvc mockMvc = standaloneSetup(controller).build();
mockMvc.perform(get("/home")).andExpect(view().name(“home”));
}

}
复制代码
那么执行Test是就会报类似错误并抛出异常:

Circular view path [home]: would dispatch back to the current handler URL [/home] again. Check your ViewResolver setup!
(Hint: This may be the result of an unspecified view, due to default view name generation.)
2. 首先,首先说下原因:


当没有声明ViewResolver时,spring会给你注册一个默认的ViewResolver,就是JstlView的实例, 该对象继承自InternalResoureView。

JstlView用来封装JSP或者同一Web应用中的其他资源,它将model对象作为request请求的属性值暴露出来, 并将该请求通过javax.servlet.RequestDispatcher转发到指定的URL.

Spring认为, 这个view的URL是可以用来指定同一web应用中特定资源的,是可以被RequestDispatcher转发的。

也就是说,在页面渲染(render)之前,Spring会试图使用RequestDispatcher来继续转发该请求。如下代码:

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.)”);
}
从这段代码可以看出,如果你的view name和你的path是相同的字符串,根据Spring的转发规则,就等于让自己转发给自己,会陷入死循环。所以Spring会检查到这种情况,于是抛出Circular view path异常。

  1. 其次,如何解决?

通过原因分析,造成问题有两个因素:1). 缺省转发, 2). view和path同名

那么消除这两个因素任何一个就可以解决这个问题。

3.1 解决办法一: 消除缺省转发

虽然在controller中已经定义了view, 但在使用Spring Test时却仍然无效,这个不知道什么原因,也许是Spring Test的Bug, 有待探究。既然无效,那就在Test中重新定义一下view

, 这样虽然麻烦点,但毕竟消除了缺省转发,所以可以解决问题。示例代码如下:

复制代码
public class TestJavaConfig {

private MockMvc mockMvc;

@InjectMocks
private StudentController studentController;

@Mock
private StudentService studentService;

@Before
public void setUp(){
    MockitoAnnotations.initMocks(this);
    InternalResourceViewResolver resolver = new InternalResourceViewResolver(); //在test中重新配置视图解析器
    resolver.setPrefix("/WEB_INF/views");
    resolver.setSuffix(".jsp");
    mockMvc = MockMvcBuilders.standaloneSetup(studentController).setViewResolvers(resolver).build();

}
@Test
public void testList()throws Exception{
    mockMvc.perform(get("/home")).andExpect(view().name("home"));
}

复制代码
3.2 解决办法二: 修改view和path,让他们不同名

这个方法最简单,建议用这种办法,比如上面的home视图, 只要我们的path不是"/home"就可以,可以改view名字(比如改成homepage),或者修改/path(比如/root).

上面是转载的内容,一下是自己代码中遇到的问题:

前端界面代码界面代码书写前端界面内容

	@GetMapping("/yj/{portalId}")
	public ModelAndView yj(ModelAndView mav, @PathVariable("portalId") String portalId) {
		if (Base64Utils.isBase64(portalId)){
			portalId =  Base64Utils.decode(portalId);
		}
		PortalEntity portalInfo = service.getPortalInfos(portalLinkPrefix + ":" + TenantUtil.TenantId(), portalId);
		mav.addObject("portalInfo",portalInfo);
		if (StringUtils.isEmpty(portalInfo.getIndexUrl())) {
			mav.setViewName("PortalIndex/county/yj");
		} else {
			mav.setViewName(portalInfo.getIndexUrl());
		}

		return mav;
	}

数据库数据在这里插入图片描述
这样的情况就会出现上述错误描述,下次写代码时一定要避免path和url相同的情况,这样造成页面视图解析器抛出异常,避免页面死循环。

发布了22 篇原创文章 · 获赞 13 · 访问量 5793

猜你喜欢

转载自blog.csdn.net/weixin_43839457/article/details/89876104