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
경우 동일한 서블릿 (이전과 동일하게 처리 되었기 때문에)이 요청을 처리하고 무한 루프에 빠진다는 것을 알고 있습니다.
ThymeleafViewResolver
a ServletContextTemplateResolver
와 a를 특정 prefix
and로 선언 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();
}
Here's an easy fix if you don't actually care about rendering the view.
Create a subclass of InternalResourceViewResolver which doesn't check for circular view paths:
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;
}
}
Then set up your test with it:
MockMvc mockMvc;
@Before
public void setUp() {
final MyController controller = new MyController();
mockMvc =
MockMvcBuilders.standaloneSetup(controller)
.setViewResolvers(new StandaloneMvcTestViewResolver())
.build();
}
If you are using Spring Boot, then add thymeleaf dependency into your pom.xml:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
I am using Spring Boot to try and load a webpage, not test, and had this problem. My solution was a bit different than those above considering the slightly different circumstances. (although those answers helpled me understand.)
I simply had to change my Spring Boot starter dependency in Maven from:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
to:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Just changing the 'web' to 'thymeleaf' fixed the problem for me.
Adding /
after /preference
solved the problem for me:
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference/"))
.andDo(print());
}
I am using Spring Boot with Thymeleaf. This is what worked for me. There are similar answers with JSP but note that I am using HTML, not JSP, and these are in the folder src/main/resources/templates
like in a standard Spring Boot project as explained here. This could also be your case.
@InjectMocks
private MyController myController;
@Before
public void setup()
{
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(myController)
.setViewResolvers(viewResolver())
.build();
}
private ViewResolver viewResolver()
{
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("classpath:templates/");
viewResolver.setSuffix(".html");
return viewResolver;
}
Hope this helps.
In my case, I was trying out Kotlin + Spring boot and I got into the Circular View Path issue. All the suggestions I got online could not help, until I tried the below:
Originally I had annotated my controller using @Controller
import org.springframework.stereotype.Controller
I then replaced @Controller
with @RestController
import org.springframework.web.bind.annotation.RestController
And it worked.
For Thymeleaf:
I just began using spring 4 and thymeleaf, when I encountered this error it was resolved by adding:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="0" />
</bean>
When using @Controller
annotation, you need @RequestMapping
and @ResponseBody
annotations. Try again after adding annotation @ResponseBody
I use the annotation to configure spring web app, the problem solved by adding a InternalResourceViewResolver
bean to the configuration. Hope it would be helpful.
@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;
}
}
This is happening because Spring is removing "preference" and appending the "preference" again making the same path as the request Uri.
Happening like this : request Uri: "/preference"
remove "preference": "/"
append path: "/"+"preference"
end string: "/preference"
This is getting into a loop which the Spring notifies you by throwing exception.
Its best in your interest to give a different view name like "preferenceView" or anything you like.
try adding compile("org.springframework.boot:spring-boot-starter-thymeleaf") dependency to your gradle file.Thymeleaf helps mapping views.
Another simple approach:
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);
}
}
'Programing' 카테고리의 다른 글
exit () 함수 사용 (0) | 2020.08.29 |
---|---|
C #을 사용하여 .net에서 RSS 피드를 읽는 가장 좋은 방법 (0) | 2020.08.29 |
한 목록에 다른 요소가 포함되어 있는지 확인 (0) | 2020.08.29 |
Vue.js를 다른 페이지로 리디렉션 (0) | 2020.08.29 |
부모의 jQuery 부모 (0) | 2020.08.29 |