我的一个控制器中有以下代码:
@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测试来测试我的应用程序?有人有任何线索吗?
答案 0 :(得分:81)
我通过使用@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) {
答案 1 :(得分:54)
这与Spring MVC测试无关。
当你没有声明ViewResolver
时,Spring会注册一个默认的InternalResourceViewResolver
,它会创建JstlView
的实例来呈现View
。
JstlView
类扩展了InternalResourceView
同一Web应用程序中的JSP或其他资源的包装器。 将模型对象公开为请求属性并转发请求 使用javax.servlet.RequestDispatcher到指定的资源URL。
此视图的网址应指定网络中的资源 应用程序,适用于RequestDispatcher的转发或包含 方法强>
大胆是我的。换句话说,在渲染之前,视图会尝试获取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
,那么同一个servlet(因为处理前一个)会处理请求,你会进入无限循环。
当您使用特定ThymeleafViewResolver
和ServletContextTemplateResolver
声明prefix
和suffix
时,它会以不同方式构建View
,为其提供类似<的路径/ p>
WEB-INF/web-templates/preference.html
ThymeleafView
个实例使用a定位相对于ServletContext
路径的文件
ServletContextResourceResolver
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
最终
return servletContext.getResourceAsStream(resourceName);
这将获得相对于ServletContext
路径的资源。然后,它可以使用TemplateEngine
生成HTML。这里无法发生无限循环。
答案 2 :(得分:31)
这就是我解决这个问题的方法:
@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();
}
答案 3 :(得分:31)
@Controller
→@RestController
我有同样的问题,我注意到我的控制器也注明了@Controller
。用@RestController
替换它解决了这个问题。以下是Spring Web MVC的解释:
@RestController是一个组合注释,它本身是元注释的 与@Controller和@ResponseBody指示一个控制器的每一个 因此,方法继承了类型级@ResponseBody注释 直接写入响应主体与视图分辨率和渲染 使用HTML模板。
答案 4 :(得分:13)
如果您真的不关心渲染视图,可以轻松解决此问题。
创建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();
}
答案 5 :(得分:11)
如果您使用的是Spring Boot,请将百万美元依赖项添加到您的pom.xml中:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
答案 6 :(得分:9)
如果您未使用@RequestBody
且仅使用@Controller
,则解决此问题的最简单方法是使用@RestController
而不是@Controller
答案 7 :(得分:9)
我正在使用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>
只需更改网络&#39;到了百里香的&#39;为我解决了这个问题。
答案 8 :(得分:3)
我正在将Spring Boot与Thymeleaf一起使用。这对我有用。 JSP也有类似的答案,但请注意,我使用的是HTML,而不是JSP,它们在文件夹src/main/resources/templates
中,就像在标准here中所述的标准Spring Boot项目中一样。这也可能是您的情况。
@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;
}
希望这会有所帮助。
答案 9 :(得分:2)
对于Thymeleaf:
我刚刚开始使用spring 4和thymeleaf,当我遇到这个错误时,它通过添加来解决:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="0" />
</bean>
答案 10 :(得分:1)
使用@Controller
注释时,您需要@RequestMapping
和@ResponseBody
注释。
添加注释@ResponseBody
答案 11 :(得分:1)
在/
之后添加/preference
为我解决了这个问题:
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference/"))
.andDo(print());
}
答案 12 :(得分:1)
将注释@ResponseBody
添加到方法返回中。
答案 13 :(得分:0)
在我的情况下,spring boot 2 和 jdk 11 中的圆形视图路径是通过重定向到 index.html 来修复的:
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/index.html");
}
};
答案 14 :(得分:0)
就我而言,在尝试使用Spring Boot应用程序为JSP页面提供服务时遇到了这个问题。
这对我有用:
application.properties
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp
pom.xml
要启用对JSP的支持,我们需要添加对tomcat-embed-jasper的依赖。
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
答案 15 :(得分:0)
运行Spring Boot + Freemarker时,如果页面出现:
Whitelabel错误页面此应用程序没有针对/错误的显式映射,因此您将其视为后备。
在spring-boot-starter-parent 2.2.1.RELEASE版本中,freemarker不起作用:
spring.freemarker.suffix = .ftl
答案 16 :(得分:0)
就我而言,我正在尝试Kotlin + Spring引导,但是遇到了“环形视图路径”问题。在尝试以下方法之前,我上网获得的所有建议都无济于事:
最初我使用@Controller
注释了我的控制器
import org.springframework.stereotype.Controller
然后我将@Controller
替换为@RestController
import org.springframework.web.bind.annotation.RestController
它奏效了。
答案 17 :(得分:0)
尝试在您的gradle文件中添加compile(“ org.springframework.boot:spring-boot-starter-thymeleaf”)依赖项。Thymeleaf帮助映射视图。
答案 18 :(得分:0)
这种情况正在发生,因为Spring正在删除&#34;偏好&#34;并附加&#34;偏好&#34;再次创建与请求Uri相同的路径。
发生这样的事: 请求Uri: &#34; /偏好&#34;
删除&#34;偏好&#34;: &#34; /&#34;
追加路径: &#34; /&#34; +&#34;优选&#34;
结束字符串: &#34; /偏好&#34;
这是一个循环,Spring通过抛出异常来通知你。
最适合您提供不同的视图名称,例如&#34; preferenceView&#34;或者你喜欢的任何东西。
答案 19 :(得分:0)
我使用注释来配置spring web app,通过在配置中添加InternalResourceViewResolver
bean来解决问题。希望它会有所帮助。
@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;
}
}
答案 20 :(得分:-2)
另一种简单方法:
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);
}
}