[spring] Spring MVC 테스트에서“Circular view path”예외를 피하는 방법

내 컨트롤러 중 하나에 다음 코드가 있습니다.

@Controller
@RequestMapping("/preference")
public class PreferenceController {

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

다음과 같이 Spring MVC 테스트 를 사용하여 테스트하려고 합니다.

@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());
    }
}

다음 예외가 발생합니다.

순환보기 경로 [preference] : 현재 핸들러 URL [/ preference]로 다시 디스패치합니다. ViewResolver 설정을 확인하십시오! (힌트 : 이것은 기본보기 이름 생성으로 인해 지정되지 않은보기의 결과 일 수 있습니다.)

이상한 점은 아래와 같이 템플릿과 뷰 리졸버를 포함하는 “전체”컨텍스트 구성로드 할 때 제대로 작동 한다는 것입니다 .

<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>

템플릿 리졸버에 의해 추가 된 접두사는 앱이이 템플릿 리졸버를 사용할 때 “원형 뷰 경로”가 없다는 것을 잘 알고 있습니다.

그렇다면 Spring MVC 테스트를 사용하여 내 앱을 어떻게 테스트해야합니까?



답변

이것은 Spring MVC 테스트와 관련이 없습니다.

당신이를 선언하지 않는 경우 ViewResolver, Spring은 기본 등록 InternalResourceViewResolver의 인스턴스를 생성 JstlView을 렌더링을 View.

JstlView클래스는 확장 InternalResourceView되는

동일한 웹 응용 프로그램 내의 JSP 또는 기타 리소스에 대한 래퍼입니다. 모델 객체를 요청 속성으로 노출하고 javax.servlet.RequestDispatcher를 사용하여 지정된 리소스 URL로 요청을 전달합니다.

이보기의 URL은 RequestDispatcher의 전달 또는 포함 메소드에 적합한 웹 애플리케이션 내의 자원을 지정해야합니다.

Bold는 내 것입니다. 즉, 렌더링하기 전에 뷰는를 가져 오려고 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. 변수 uri는 처리되는 요청의 URI를 보유합니다 /context/preference.

위의 코드는으로 전달하는 /context/preference경우 동일한 서블릿 (동일한 서블릿이 이전에 처리되었으므로)이 요청을 처리하고 무한 루프에 빠진다는 것을 알고 있습니다.


ThymeleafViewResolvera ServletContextTemplateResolver와 a를 특정 prefixand로 선언 suffix하면 View다르게 빌드되어 다음 과 같은 경로를 제공합니다.

WEB-INF/web-templates/preference.html

ThymeleafView인스턴스는 다음 ServletContext을 사용하여 경로에 상대적인 파일을 찾습니다 .
ServletContextResourceResolver

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

결국

return servletContext.getResourceAsStream(resourceName);

ServletContext경로에 상대적인 리소스를 가져옵니다 . 그런 다음을 사용 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) {


답변

@Controller@RestController

나는 같은 문제가 있었고 내 컨트롤러에도 @Controller. 교체 @RestController하면 문제 가 해결되었습니다. 다음은 Spring Web MVC 의 설명입니다 .

@RestController는 자체적으로 @Controller 및 @ResponseBody로 메타 주석이 추가 된 구성된 주석으로, 모든 메서드가 유형 수준 @ResponseBody 주석을 상속하므로 응답 본문에 직접 쓰기와 HTML 템플릿을 사용한 렌더링 비교를 나타냅니다.


답변

이것이 내가이 문제를 해결 한 방법입니다.

@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();
    }


답변

나는 Spring Boot를 사용하여 테스트가 아닌 웹 페이지를 시도하고로드하고 있으며이 문제가 발생했습니다. 내 솔루션은 약간 다른 상황을 고려할 때 위의 솔루션과 약간 달랐습니다. (그 답변이 이해하는 데 도움이되었지만)

Maven에서 Spring Boot 스타터 종속성을 다음과 같이 변경해야했습니다.

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
</dependency>

에:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

‘웹’을 ‘thymeleaf’로 변경하면 문제가 해결되었습니다.


답변

실제로 뷰 렌더링에 신경 쓰지 않는 경우 쉽게 수정할 수 있습니다.

순환보기 경로를 확인하지 않는 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;
    }
}

그런 다음 테스트를 설정하십시오.

MockMvc mockMvc;

@Before
public void setUp() {
    final MyController controller = new MyController();

    mockMvc =
            MockMvcBuilders.standaloneSetup(controller)
                    .setViewResolvers(new StandaloneMvcTestViewResolver())
                    .build();
}


답변

Spring Boot를 사용하는 경우 pom.xml에 thymeleaf 종속성을 추가하십시오.

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>2.1.6.RELEASE</version>
    </dependency>