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?
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.
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) {
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();
}
@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.
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();
}
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>
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.
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>
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.
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.
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.
versuchen Sie, die Kompilierungsabhängigkeit ("org.springframework.boot: spring-boot-starter-thymeleaf") zu Ihrer Gradle-Datei hinzuzufügen.
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.
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;
}
}