如何避免Spring MVC测试的"循环视图路径"异常

bal*_*teo 96 spring spring-mvc circular-reference thymeleaf spring-mvc-test

我的一个控制器中有以下代码:

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

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

我只是尝试使用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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我收到以下异常:

循环视图路径[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>
Run Code Online (Sandbox Code Playgroud)

我很清楚,当应用程序使用此模板解析程序时,模板解析程序添加的前缀可确保没有"循环视图路径".

但那么我应该如何使用Spring MVC测试来测试我的应用程序?有人有任何线索吗?

小智 86

我使用@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) {
Run Code Online (Sandbox Code Playgroud)

  • 他们希望通过解析视图来返回HTML,而不是返回`List <DomainObject>`的序列化版本. (9认同)
  • 这为Spring rest Web服务返回JSON响应时解决了我的问题。 (2认同)

Sot*_*lis 59

这与Spring MVC测试无关.

当你没有声明a时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.)");
}
Run Code Online (Sandbox Code Playgroud)

path视图名称在哪里,从中返回的是什么@Controller.在这个例子中,就是这样preference.该变量uri保存正在处理的请求的uri,即/context/preference.

上面的代码意识到,如果你要转发/context/preference,同一个servlet(因为处理前一个)将处理请求,你将进入无限循环.


在声明ThymeleafViewResolverServletContextTemplateResolver与特定的prefixsuffix,它构建的View方式不同,给它像一个路径

WEB-INF/web-templates/preference.html
Run Code Online (Sandbox Code Playgroud)

ThymeleafView实例ServletContext使用a 定位文件相对于路径 ServletContextResourceResolver

templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
Run Code Online (Sandbox Code Playgroud)

最终

return servletContext.getResourceAsStream(resourceName);
Run Code Online (Sandbox Code Playgroud)

这将获得相对于ServletContext路径的资源.然后它可以使用它TemplateEngine来生成HTML.这里无法发生无限循环.

  • @ShirgillFarhanAnsari 带有`String` 返回类型(并且没有`@ResponseBody`)的`@RequestMapping` 注释处理程序方法的返回值由`ViewNameMethodReturnValueHandler` 处理,该方法将String 解释为视图名称,并使用它进行处理我在回答中解释的过程。使用`@ResponseBody`,Spring MVC 将改为使用`RequestResponseBodyMethodProcessor`,而是将字符串直接写入 HTTP 响应,即。没有视图分辨率。 (3认同)
  • @balteo要测试您的应用程序,请提供正确的`ViewResolver`.您问题中的`ThymeleafViewResolver`,您自己配置的`InternalResourceViewResolver`或更改您在控制器中返回的视图名称. (2认同)

Bor*_*ris 48

@Controller@RestController

我有同样的问题,我注意到我的控制器也注释了@Controller.更换它@RestController解决了这个问题.以下是Spring Web MVC的解释:

@RestController是一个组合注释,它本身用@Controller和@ResponseBody进行元注释,表示一个控制器,它的每个方法都继承了类型级@ResponseBody注释,因此直接写入响应体vs视图分辨率并使用HTML模板进行渲染.

  • 也为我工作过。我有一个“@ControllerAdvice”,其中有一个“handleXyException”方法,它返回我自己的对象而不是 ResponseEntity。在“@ControllerAdvice”注释之上添加“@RestController”有效,问题就消失了。 (3认同)

Pio*_*ara 33

这就是我解决这个问题的方法:

@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();
    }
Run Code Online (Sandbox Code Playgroud)

  • 在帮助某人在其新的单元测试中解决此问题时,这正是我们所需要的。 (2认同)

Dav*_*wer 15

如果您实际上并不关心渲染视图,这是一个简单的修复.

创建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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用它设置你的测试:

MockMvc mockMvc;

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

    mockMvc =
            MockMvcBuilders.standaloneSetup(controller)
                    .setViewResolvers(new StandaloneMvcTestViewResolver())
                    .build();
}
Run Code Online (Sandbox Code Playgroud)


Sar*_*oev 13

如果您使用的是Spring Boot,请将百万美元依赖项添加到您的pom.xml中:

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>2.1.6.RELEASE</version>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

  • 投票赞成。缺少 Thymeleaf 依赖项是导致我的项目中出现此错误的原因。但是,如果您使用的是 Spring Boot,那么依赖项将如下所示:`&lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt; &lt;/依赖关系&gt;` (2认同)

Old*_*led 12

我使用Spring Boot尝试加载网页,而不是测试,并遇到了这个问题.考虑到略有不同的情况,我的解决方案与上述方案略有不同.(虽然这些答案帮助我理解.)

我只需要在Maven中更改我的Spring Boot启动器依赖项:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

至:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)

只是将'网络'改为'百里香',为我解决了问题.

  • 我也遇到了这个问题,尝试了这个解决方案,但遇到了丢失文件的问题,例如缺少 javax.validation.constraints。我的解决方案是包含 thymeleaf 和 web jar 来解决所有问题 (2认同)

Moz*_*ath 11

如果您还没有使用 a@RequestBody并且只使用@Controller,则解决此问题的最简单方法是使用@RestController而不是@Controller

  • 这不是修复,现在它会显示您的文件名,而不是显示模板 (2认同)

小智 9

为我解决问题/后添加/preference

@Test
public void circularViewPathIssue() throws Exception {
    mockMvc.perform(get("/preference/"))
           .andDo(print());
}
Run Code Online (Sandbox Code Playgroud)


小智 8

就我而言,我正在尝试 Kotlin + Spring boot,但遇到了 Circular View Path 问题。我在网上得到的所有建议都无济于事,直到我尝试了以下方法:

最初我使用注释我的控制器 @Controller

import org.springframework.stereotype.Controller

然后我替换@Controller@RestController

import org.springframework.web.bind.annotation.RestController

它奏效了。