J'ai le code suivant dans l'un de mes contrôleurs:
@Controller
@RequestMapping("/preference")
public class PreferenceController {
@RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String preference() {
return "preference";
}
}
J'essaye simplement de le tester en utilisant Spring MVC test 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());
}
}
Je reçois l'exception suivante:
Chemin de vue circulaire [préférence]: renverrait au courant URL du gestionnaire [/ préférence] à nouveau. Vérifiez votre configuration ViewResolver! (Conseil: Cela peut être le résultat d'une vue non spécifiée, en raison de la vue par défaut Nom génération.)
Ce que je trouve étrange, c’est que cela fonctionne bien lorsque je charge la configuration de contexte "complète" qui inclut le modèle et les résolveurs de vues 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 comment puis-je tester mon application à l'aide du test Spring MVC? Quelqu'un a-t-il un indice?
Cela n'a rien à voir avec les tests Spring MVC.
Lorsque vous ne déclarez pas ViewResolver
, Spring enregistre une valeur par défaut InternalResourceViewResolver
qui crée des instances de JstlView
pour le rendu de View
.
La classe JstlView
étend InternalResourceView
qui est
Wrapper pour un JSP ou une autre ressource au sein de la même application Web . Expose les objets de modèle en tant qu'attributs de requête et transmet la requête à l'URL de la ressource spécifiée à l'aide de javax.servlet.RequestDispatcher.
Une URL pour cette vue est supposée spécifier une ressource sur le Web application, appropriée pour RequestDispatcher's forward ou include méthode.
Bold est à moi. En d'autres termes, la vue, avant le rendu, essaiera d'obtenir un RequestDispatcher
auquel forward()
. Avant de faire cela, il vérifie ce qui suit
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.)");
}
où path
est le nom de la vue, ce que vous avez renvoyé du @Controller
. Dans cet exemple, il s'agit de preference
. La variable uri
contient l'URI de la demande en cours de traitement, qui est /context/preference
.
Le code ci-dessus réalise que si vous deviez transmettre à /context/preference
, le même servlet (puisque le même manipulé le précédent) traiterait la demande et vous iriez dans une boucle sans fin.
Lorsque vous déclarez un ThymeleafViewResolver
et un ServletContextTemplateResolver
avec un prefix
et un suffix
spécifiques, il construit le View
différemment, en lui donnant un chemin tel que
WEB-INF/web-templates/preference.html
Les instances ThymeleafView
localisent le fichier par rapport au chemin ServletContext
à l'aide de la variable ServletContextResourceResolver
.
templateInputStream = resourceResolver.getResourceAsStream(templateProcessingParameters, resourceName);`
qui finalement
return servletContext.getResourceAsStream(resourceName);
Cela obtient une ressource qui est relative au chemin ServletContext
. Il peut ensuite utiliser TemplateEngine
pour générer le code HTML. Il est impossible qu'une boucle sans fin se produise ici.
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) {
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();
}
@Controller
→ @RestController
J'avais le même problème et j'ai remarqué que mon contrôleur était également annoté avec @Controller
. Le remplacer 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 Cette méthode hérite de l'annotation @ResponseBody de niveau type et, par conséquent, de écrit directement dans le corps de la réponse par rapport à la résolution de vue et au rendu avec un modèle HTML.
Voici une solution facile si vous ne vous souciez pas vraiment de rendre la vue.
Créez une sous-classe de InternalResourceViewResolver qui ne vérifie pas les chemins de vue circulaire:
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;
}
}
Ensuite, configurez votre test avec:
MockMvc mockMvc;
@Before
public void setUp() {
final MyController controller = new MyController();
mockMvc =
MockMvcBuilders.standaloneSetup(controller)
.setViewResolvers(new StandaloneMvcTestViewResolver())
.build();
}
Si vous utilisez Spring Boot, ajoutez une dépendance thymeleaf dans votre pom.xml:
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
J'utilise Spring Boot pour essayer de charger une page Web, pas pour tester, et j'ai eu ce problème. Ma solution était un peu différente de celles présentées ci-dessus, compte tenu des circonstances légèrement différentes. (bien que ces réponses m'ont aidé à comprendre.)
Je devais simplement changer ma dépendance du 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>
Changer simplement le 'web' en 'thymeleaf' a corrigé le problème pour moi.
Pour Thymeleaf:
J'ai juste commencé à utiliser Spring 4 et thymeleaf. Lorsque j'ai rencontré cette erreur, elle a été résolue en ajoutant:
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
<property name="order" value="0" />
</bean>
Lorsque vous utilisez l'annotation @Controller
, vous avez besoin des annotations @RequestMapping
et @ResponseBody
. Essayez de nouveau après avoir ajouté l'annotation @ResponseBody
J'utilise Spring Boot avec Thymeleaf. C'est ce qui a fonctionné pour moi. Il existe des réponses similaires avec JSP mais notez que j'utilise HTML, et non JSP, et que celles-ci se trouvent dans le dossier src/main/resources/templates
, comme dans un projet Spring Boot standard, comme expliqué here . Cela pourrait aussi être votre cas.
@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;
}
J'espère que cela t'aides.
Dans mon cas, j'essayais la botte Kotlin + Spring et je suis entré dans le problème de la vue circulaire. Toutes les suggestions que j'ai eues en ligne ne pourraient pas m'aider, jusqu'à ce que j'aie essayé ce qui suit:
A l'origine, j'avais annoté mon contrôleur en utilisant @Controller
import org.springframework.stereotype.Controller
J'ai ensuite remplacé @Controller
par @RestController
import org.springframework.web.bind.annotation.RestController
Et ça a fonctionné.
essayez d’ajouter une dépendance compile ("org.springframework.boot: spring-boot-starter-thymeleaf") à votre fichier de classement. Thymeleaf aide à mapper les vues.
Ceci est dû au fait que Spring supprime la "préférence" et l'ajoute à nouveau, créant ainsi le même chemin que la requête Uri.
Cela se passe comme ceci: Request Uri: "/ Preference"
supprimer "préférence": "/"
append path: "/" + "préférence"
chaîne de fin: "/ préférence"
Cela entre dans une boucle que le ressort vous notifie en lançant une exception.
Il est de votre intérêt de donner un nom de vue différent tel que "préférence" ou ce que vous préférez.
Ajouter /
après /preference
a résolu le problème pour moi:
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference/"))
.andDo(print());
}
J'utilise l'annotation pour configurer l'application Web Spring, le problème a été résolu en ajoutant un bean InternalResourceViewResolver
à la configuration. J'espère que ce serait utile.
@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;
}
}