164 votes

Comment éviter l'exception " Circular view path " avec le test Spring MVC

J'ai le code suivant dans un de mes contrôleurs :

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

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

J'essaie simplement de le tester en utilisant Test de Spring MVC comme suit :

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

J'obtiens l'exception suivante :

Chemin d'accès circulaire [préférence] : renvoie à l'URL du gestionnaire actuel [/préférence]. gestionnaire actuel [/préférence]. Vérifiez la configuration de votre ViewResolver ! (Indice : Cela peut être le résultat d'une vue non spécifiée, en raison de la vue par défaut. par défaut).

Ce que je trouve étrange, c'est que cela fonctionne bien lorsque je charge la configuration contextuelle "complète". qui inclut le modèle et les résolveurs de vue comme indiqué ci-dessous :

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

Je suis bien conscient que le préfixe ajouté par le résolveur de modèle garantit qu'il n'y a pas de "chemin de vue circulaire" lorsque l'application utilise ce résolveur de modèle.

Mais alors comment suis-je censé tester mon application en utilisant le test Spring MVC ?

185voto

user3301492 Points 1087

@Controller @RestController

J'ai eu le même problème et j'ai remarqué que mon contrôleur était également annoté avec @Controller . En le remplaçant par @RestController a résolu le problème. Voici l'explication de Spring Web MVC :

@RestController est une annotation composée qui est elle-même méta-annotée avec @Controller et @ResponseBody indiquant un contrôleur dont chaque méthode hérite de l'annotation @ResponseBody au niveau du type et donc écrit directement dans le corps de la réponse par rapport à la résolution et au rendu de la vue avec un modèle HTML.

109voto

Deepti Kohli Points 354

J'ai résolu ce problème en utilisant @ResponseBody comme ci-dessous :

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

77voto

Sotirios Delimanolis Points 77933

Cela n'a rien à voir avec les tests de Spring MVC.

Lorsque vous ne déclarez pas un ViewResolver Spring enregistre une valeur par défaut InternalResourceViewResolver qui crée des instances de JstlView pour rendre le View .

El JstlView classe s'étend InternalResourceView qui est

Wrapper pour un JSP ou une autre ressource au sein de la même application web. Il expose les objets du modèle en tant qu'attributs de la demande et transmet la demande. à l'URL de la ressource spécifiée en utilisant un javax.servlet.RequestDispatcher.

Une URL pour cette vue est censée spécifier une ressource dans le web qui convient à la méthode forward ou include de RequestDispatcher. de RequestDispatcher.

C'est moi qui souligne. En d'autres termes, la vue, avant le rendu, essaiera d'obtenir une RequestDispatcher à laquelle à forward() . Avant de procéder, il vérifie les éléments suivants

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

donde path est le nom de la vue, ce que vous avez retourné de la fonction @Controller . Dans cet exemple, c'est preference . La variable uri contient l'uri de la requête en cours de traitement, qui est /context/preference .

Le code ci-dessus réalise que si vous transmettez à /context/preference Dans ce cas, la même servlet (puisque c'est la même qui a traité la précédente) traiterait la demande et vous vous retrouveriez dans une boucle sans fin.


Lorsque vous déclarez un ThymeleafViewResolver et un ServletContextTemplateResolver avec un prefix y suffix il construit le View différemment, en lui donnant un chemin comme

WEB-INF/web-templates/preference.html

ThymeleafView les instances localisent le fichier par rapport au ServletContext en utilisant un ServletContextResourceResolver

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

qui finalement

return servletContext.getResourceAsStream(resourceName);

Cela permet d'obtenir une ressource relative à l'adresse ServletContext chemin. Il peut alors utiliser le TemplateEngine pour générer le HTML. Il est impossible qu'une boucle sans fin se produise ici.

45voto

Piotr Sagalara Points 864

Voici comment j'ai résolu ce problème :

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

Vous pouvez également créer un bean pour cela dans le fichier .xml.

<bean id = "viewResolver" class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/view/"/>
    <property name="suffix" value=".jsp"/>
</bean

32voto

Old Schooled Points 551

J'utilise Spring Boot pour essayer de charger une page Web, et non pour la tester, et j'ai rencontré ce problème. Ma solution était un peu différente de celles ci-dessus compte tenu des circonstances légèrement différentes. (bien que ces réponses m'aient aidé à comprendre).

J'ai simplement dû changer la dépendance de mon démarreur Spring Boot dans Maven. de :

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

Le fait de remplacer le mot "web" par "thymeleaf" a réglé le problème pour moi.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X