1. 程式人生 > >如何在Spring MVC 用MockMcv Test中避免”Circular view path” 異常

如何在Spring MVC 用MockMcv Test中避免”Circular view path” 異常

1. 問題的現象

@Configuration
@EnableWebMvc //啟用SpringMVC
@ComponentScan("spittr.web")
public class WebConfig  extends WebMvcConfigurerAdapter {
    @Bean  //配置JSP檢視解析器
    public ViewResolver viewResolver(){
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }

    @Override  //配置靜態資源的處理
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }
}

然後定義了一個controller,URL路徑為"/home", 它返回名字叫home的view

@Controller
public class HomeController {
    @RequestMapping(value = "/homePage", method = GET)
    public String home(){
        return "home";
    }
}

然後是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"));
        //assertEquals("home", controller.home());
    }
}

那麼執行Test是就會報類似錯誤並丟擲異常:

javax.servlet.ServletException: 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異常。

3. 其次,如何解決?

通過原因分析,造成問題有兩個因素: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).

親測可用