我的一个控制器中有以下代码:
@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
适用于同一Web应用程序中的JSP或其他资源的包装器。将模型对象公开为请求属性,并使用javax.servlet.RequestDispatcher将请求转发到指定的资源URL。
此视图的URL应该指定Web应用程序中的资源,适用于RequestDispatcher的forward或include方法。
大胆是我的。换句话说,在渲染之前,视图将尝试获取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
,给它一条路径
WEB-INF/web-templates/preference.html
ThymeleafView
实例使用ServletContext
找到相对于ServletContextResourceResolver
路径的文件
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
最终
return servletContext.getResourceAsStream(resourceName);
这将获得相对于ServletContext
路径的资源。然后它可以使用TemplateEngine
生成HTML。这里无法发生无限循环。
Thimeleaf堡:
我刚刚开始使用spring 4和thymeleaf,当我遇到这个错误时,通过添加以下内容解决了:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="0" />
</bean>
使用@Controller
注释时,需要@RequestMapping
和@ResponseBody
注释。添加注释@ResponseBody
后再试一次
我使用注释来配置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;
}
}
这种情况正在发生,因为Spring正在删除“首选项”并再次附加“首选项”,使其与请求Uri具有相同的路径。
发生这样的事情:请求Uri:“/ preference”
删除“首选项”:“/”
追加路径:“/”+“偏好”
结束字符串:“/ preference”
这是一个循环,Spring通过抛出异常来通知你。
为您提供不同的视图名称,例如“preferenceView”或您喜欢的任何内容,最符合您的利益。
尝试将compile(“org.springframework.boot:spring-boot-starter-thymeleaf”)依赖项添加到gradle文件中.Thymeleaf帮助映射视图。
就我而言,我正在尝试Kotlin + Spring启动,我进入了循环视图路径问题。我上网的所有建议都无济于事,直到我尝试了以下内容:
最初我使用@Controller
注释了我的控制器
import org.springframework.stereotype.Controller
然后我用@Controller
取代了@RestController
import org.springframework.web.bind.annotation.RestController
它奏效了。
另一个简单方法:
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);
}
}
我使用@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注释,因此直接写入响应体vs视图分辨率并使用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();
}
如果您实际上并不关心渲染视图,这是一个简单的修复。
创建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中:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
我使用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的Spring Boot。这对我有用。 JSP有类似的答案,但请注意我使用的是HTML,而不是JSP,这些文件位于src/main/resources/templates
文件夹中,就像标准的Spring Boot项目一样,如here所述。这也可能是你的情况。
@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;
}
希望这可以帮助。
在/
之后添加/preference
解决了我的问题:
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference/"))
.andDo(print());
}