wake-up-neo.com

So vermeiden Sie die Ausnahme "Circular View Path" mit dem Spring MVC-Test

Ich habe den folgenden Code in einem meiner Controller:

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

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

Ich versuche es einfach mit Spring MVC test wie folgt zu testen:

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

Ich bekomme die folgende Ausnahme:

Zirkularer Ansichtspfad [Präferenz]: würde an das aktuelle .__ zurückschicken. Handler-URL [/ preference] erneut. Überprüfen Sie Ihr ViewResolver-Setup! (Hinweis: Dies kann das Ergebnis einer nicht angegebenen Ansicht sein, aufgrund der Standardansicht Namensgenerierung.)

Ich finde es seltsam, dass es funktioniert gut, wenn ich die "vollständige" Kontextkonfiguration lade die die Vorlage und die Ansichtsauflöser wie unten gezeigt enthält:

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

Ich bin mir bewusst, dass das durch den Vorlagenauflöser hinzugefügte Präfix gewährleistet, dass es keinen "kreisförmigen Ansichtspfad" gibt, wenn die Anwendung diesen Vorlagenauflöser verwendet. 

Aber wie soll ich dann meine App mit Spring MVC testen? Hat jemand eine Ahnung?

76
balteo

Dies hat nichts mit Spring MVC-Tests zu tun.

Wenn Sie kein ViewResolver deklarieren, registriert Spring einen Standard InternalResourceViewResolver, der Instanzen von JstlView zum Rendern des View erstellt.

Die JstlView-Klasse erweitert InternalResourceView

Wrapper für eine JSP oder andere Ressource in derselben Webanwendung . Macht Modellobjekte als Anforderungsattribute verfügbar und leitet die Anforderung weiter zur angegebenen Ressourcen-URL mit einem javax.servlet.RequestDispatcher.

Eine URL für diese Ansicht soll eine Ressource im Web angeben Anwendung, geeignet für den RequestDispatcher-Forward oder Include Methode.

Fett ist mein. Mit anderen Worten, die Ansicht versucht vor dem Rendern ein RequestDispatcher zu erhalten, zu dem forward() gehört. Bevor dies geschieht, prüft es Folgendes

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.)");
}

dabei ist path der Ansichtsname, den Sie vom @Controller zurückgegeben haben. In diesem Beispiel ist das preference. Die Variable uri enthält den URI der bearbeiteten Anforderung, nämlich /context/preference

Der obige Code erkennt, dass, wenn Sie an /context/preference weiterleiten würden, dasselbe Servlet (da dasselbe dasselbe wie zuvor behandelt hat) die Anforderung bearbeiten würde und Sie in eine Endlosschleife gehen würden.


Wenn Sie ein ThymeleafViewResolver und ein ServletContextTemplateResolver mit einem bestimmten prefix und suffix deklarieren, wird das View anders erstellt, sodass ein ähnlicher Pfad entsteht

WEB-INF/web-templates/preference.html

ThymeleafView-Instanzen suchen die Datei relativ zum ServletContext-Pfad mithilfe einer ServletContextResourceResolver-Datei.

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

welche schließlich

return servletContext.getResourceAsStream(resourceName);

Dadurch wird eine Ressource abgerufen, die relativ zum ServletContext-Pfad ist. Dann kann es mit TemplateEngine den HTML-Code erzeugen. Es kann keine endlose Schleife hier passieren.

51

Ich habe dieses Problem mit @ResponseBody wie folgt gelöst:

@RequestMapping(value = "/resturl", method = RequestMethod.GET, produces = {"application/json"})
    @ResponseStatus(HttpStatus.OK)
    @Transactional(value = "jpaTransactionManager")
    public @ResponseBody List<DomainObject> findByResourceID(@PathParam("resourceID") String resourceID) {
74
Deepti Kohli

So habe ich dieses Problem gelöst:

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

@Controller@RestController

Ich hatte das gleiche Problem und bemerkte, dass mein Controller auch mit @Controller versehen war. Durch Ersetzen durch @RestController wurde das Problem behoben. Hier ist die Erklärung von Spring Web MVC :

@RestController ist eine zusammengesetzte Anmerkung, die selbst meta-kommentiert ist wobei @Controller und @ResponseBody einen Controller angeben, dessen jeder Die Methode erbt die @ResponseBody-Annotation auf Typebene und daher schreibt direkt in den Antworttext vs. Ansichtsauflösung und Rendering mit einer HTML-Vorlage.

20
Boris

Hier ist eine einfache Lösung, wenn Sie sich nicht für das Rendern der Ansicht interessieren.

Erstellen Sie eine Unterklasse von InternalResourceViewResolver, die nicht nach kreisförmigen Ansichtspfaden sucht:

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

Dann richten Sie Ihren Test damit ein:

MockMvc mockMvc;

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

    mockMvc =
            MockMvcBuilders.standaloneSetup(controller)
                    .setViewResolvers(new StandaloneMvcTestViewResolver())
                    .build();
}
13
Dave Bower

Wenn Sie Spring Boot verwenden, fügen Sie in Ihre pom.xml eine Thymeleaf-Abhängigkeit ein: 

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

Ich benutze Spring Boot, um eine Webseite zu testen und nicht zu testen, und hatte dieses Problem. Meine Lösung war etwas anders als die obigen, wenn man die etwas anderen Umstände bedenkt. (obwohl diese Antworten mir geholfen haben zu verstehen.) 

Ich musste einfach meine Spring Boot-Starterabhängigkeit in Maven Von:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
</dependency>

zu:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Durch das Ändern des "Web" in "Thymeleaf" wurde das Problem für mich behoben. 

7
Old Schooled

Für Thymeleaf:

Ich habe gerade erst mit Spring 4 und Thymeleaf angefangen. Als ich auf diesen Fehler stieß, wurde er durch Folgendes behoben:

<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
  <property name="templateEngine" ref="templateEngine" />
  <property name="order" value="0" />
</bean> 
2

Wenn Sie die Annotation @Controller verwenden, benötigen Sie die Annotationen @RequestMapping und @ResponseBody. Versuchen Sie es erneut, nachdem Sie die Anmerkung @ResponseBody hinzugefügt haben.

1
Gowri Ayyanar

Ich benutze Spring Boot mit Thymeleaf. Das hat bei mir funktioniert. Es gibt ähnliche Antworten mit JSP. Beachten Sie jedoch, dass ich HTML und nicht JSP verwende. Diese befinden sich im Ordner src/main/resources/templates wie in einem Standard-Spring-Boot-Projekt (siehe here ). Dies könnte auch Ihr Fall sein.

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

Hoffe das hilft.

1
Pedro Lopez

In meinem Fall habe ich den Kotlin + Spring-Schuh ausprobiert und mich mit dem Thema Circular View Path befasst. Alle Vorschläge, die ich online bekam, konnten nicht helfen, bis ich das Folgende ausprobierte:

Ursprünglich hatte ich meinen Controller mit @Controller kommentiert.

import org.springframework.stereotype.Controller

Ich habe dann @Controller durch @RestController ersetzt

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

Und es hat funktioniert.

1
johnmilimo

versuchen Sie, die Kompilierungsabhängigkeit ("org.springframework.boot: spring-boot-starter-thymeleaf") zu Ihrer Gradle-Datei hinzuzufügen.

0
aishwarya kore

Dies geschieht, weil Spring die "Voreinstellung" entfernt und die "Voreinstellung" erneut anfügt, wodurch derselbe Pfad wie in der Anfrage-Uri verwendet wird. 

Dies geschieht folgendermaßen: Anfrage Uri: "/ Einstellung"

entferne "Voreinstellung": "/"

pfad anhängen: "/" + "Einstellung"

end string: "/ preference"

Dies ist in eine Schleife geraten, die der Frühling Sie durch das Auslösen einer Ausnahme benachrichtigt. 

Es ist am besten in Ihrem Interesse, einen anderen Ansichtsnamen wie "preferenceView" oder alles, was Sie möchten, anzugeben.

0
xpioneer

Das Hinzufügen von / nach /preference hat das Problem für mich gelöst:

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

Ich verwende die Anmerkung, um die Spring-Web-App zu konfigurieren. Das Problem wurde durch Hinzufügen einer InternalResourceViewResolver-Bean zur Konfiguration gelöst. Ich hoffe es wäre hilfreich.

@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;
    }
}
0
alijandro