如何使用 Spring MVC 测试避免“圆形视图路径”异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18813615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 06:14:06  来源:igfitidea点击:

How to avoid the "Circular view path" exception with Spring MVC test

springspring-mvccircular-referencethymeleafspring-mvc-test

提问by balteo

I have the following code in one of my controllers:

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

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

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

I am simply trying to test it using Spring MVC testas follows:

我只是尝试使用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());
    }
}

I am getting the following exception:

我收到以下异常:

Circular view path [preference]: would dispatch back to the current handler URL [/preference] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

圆形视图路径 [preference]:将再次分派回当前处理程序 URL [/preference]。检查您的 ViewResolver 设置!(提示:由于默认视图名称生成,这可能是未指定视图的结果。)

What I find strange is that it works fine when I load the "full" context configurationthat includes the template and view resolvers as shown below:

我觉得奇怪的是,当我加载包含模板和视图解析器的“完整”上下文配置时它工作正常,如下所示:

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

I am well aware that the prefix added by the template resolver ensures that there is not "circular view path" when the app uses this template resolver.

我很清楚模板解析器添加的前缀可确保应用程序使用此模板解析器时没有“圆形视图路径”。

But then how I am supposed to test my app using Spring MVC test?

但是,我应该如何使用 Spring MVC 测试来测试我的应用程序?

采纳答案by Sotirios Delimanolis

This has nothing to do with Spring MVC testing.

这与 Spring MVC 测试无关。

When you don't declare a ViewResolver, Spring registers a default InternalResourceViewResolverwhich creates instances of JstlViewfor rendering the View.

当您不声明 a 时ViewResolver,Spring 会注册一个默认值InternalResourceViewResolver,该默认值创建JstlView用于呈现View.

The JstlViewclass extends InternalResourceViewwhich is

JstlView类扩展InternalResourceView其是

Wrapper for a JSP or other resource within the same web application. Exposes model objects as request attributes and forwards the request to the specified resource URL using a javax.servlet.RequestDispatcher.

A URL for this view is supposed to specify a resource within the web application, suitable for RequestDispatcher's forward or include method.

同一 Web 应用程序中 JSP 或其他资源的包装器。将模型对象作为请求属性公开,并使用 javax.servlet.RequestDispatcher 将请求转发到指定的资源 URL。

此视图的 URL 应该指定 Web 应用程序中的资源,适用于 RequestDispatcher 的转发或包含方法。

Bold is mine. In otherwords, the view, before rendering, will try to get a RequestDispatcherto which to forward(). Before doing this it checks the following

大胆是我的。换句话说,视图在渲染之前会尝试获取 aRequestDispatcher到 which 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.)");
}

where pathis the view name, what you returned from the @Controller. In this example, that is preference. The variable uriholds the uri of the request being handled, which is /context/preference.

path视图名称在哪里,您从@Controller. 在本例中,即preference. 该变量uri保存正在处理的请求的 uri,即/context/preference.

The code above realizes that if you were to forward to /context/preference, the same servlet (since the same handled the previous) would handle the request and you would go into an endless loop.

上面的代码意识到,如果您要转发到/context/preference,则相同的 servlet(因为与前面处理的相同)将处理请求,并且您将进入无限循环。



When you declare a ThymeleafViewResolverand a ServletContextTemplateResolverwith a specific prefixand suffix, it builds the Viewdifferently, giving it a path like

当你用特定的and声明 aThymeleafViewResolver和 aServletContextTemplateResolver时,它会构建不同的,给它一个类似的路径prefixsuffixView

WEB-INF/web-templates/preference.html

ThymeleafViewinstances locate the file relative to the ServletContextpath by using a ServletContextResourceResolver

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

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

which eventually

最终

return servletContext.getResourceAsStream(resourceName);

This gets a resource that is relative to the ServletContextpath. It can then use the TemplateEngineto generate the HTML. There's no way an endless loop can happen here.

这将获取与ServletContext路径相关的资源。然后它可以使用TemplateEngine来生成 HTML。这里不可能发生无限循环。

回答by Deepti Kohli

I solved this problem by using @ResponseBody like below:

我通过使用@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) {

回答by Boris

@Controller@RestController

@Controller@RestController

I had the same issue and I noticed that my controller was also annotated with @Controller. Replacing it with @RestControllersolved the issue. Here is the explanation from Spring Web MVC:

我遇到了同样的问题,我注意到我的控制器也用@Controller. 替换它@RestController解决了问题。这是Spring Web MVC的解释:

@RestController is a composed annotation that is itself meta-annotated with @Controller and @ResponseBody indicating a controller whose every method inherits the type-level @ResponseBody annotation and therefore writes directly to the response body vs view resolution and rendering with an HTML template.

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

回答by Piotr Sagalara

This is how I solved this problem:

我是这样解决这个问题的:

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

回答by Old Schooled

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

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

I simply had to change my Spring Boot starter dependency in Maven from:

我只需要从以下位置更改我在 Maven 中的 Spring Boot starter 依赖项:

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

只需将“网络”更改为“百里香叶”即可为我解决问题。

回答by Dave Bower

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:

创建不检查圆形视图路径的 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;
    }
}

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

回答by Sarvar Nishonboev

If you are using Spring Boot, then add thymeleaf dependency into your pom.xml:

如果您使用的是 Spring Boot,则将 thymeleaf 依赖项添加到您的 pom.xml 中:

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

回答by johnmilimo

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:

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

Originally I had annotated my controller using @Controller

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

import org.springframework.stereotype.Controller

import org.springframework.stereotype.Controller

I then replaced @Controllerwith @RestController

然后我替换@Controller@RestController

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

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

And it worked.

它奏效了。

回答by Svetlana Mitrakhovich

Adding /after /preferencesolved the problem for me:

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

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

回答by Ishaan Arora

Add the annotation @ResponseBodyto your method return.

将注释添加@ResponseBody到您的方法返回。