大坑”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相同的情況,這樣造成頁面視圖解析器拋出異常,避免頁面死循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章