Cómo evitar la excepción “Ruta de vista circular” con la prueba Spring MVC

Tengo el siguiente código en uno de mis controladores:

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

Simplemente estoy tratando de probarlo usando la prueba Spring MVC de la siguiente manera:

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

Recibo la siguiente excepción:

Ruta de vista circular [preferencia]: se volvería a enviar a la URL del controlador actual [/ preference]. ¡Comprueba tu configuración de ViewResolver! (Sugerencia: puede ser el resultado de una vista no especificada, debido a la generación de nombre de vista predeterminada).

Lo que me parece extraño es que funciona bien cuando cargo la configuración de contexto “completa” que incluye la plantilla y los resolvedores de vista como se muestra a continuación:

         

Soy muy consciente de que el prefijo agregado por la resolución de la plantilla garantiza que no haya una “ruta de vista circular” cuando la aplicación utiliza esta resolución de plantilla.

Pero ¿cómo se supone que debo probar mi aplicación usando la prueba Spring MVC? Alguien tiene alguna pista?

Esto no tiene nada que ver con las pruebas Spring MVC.

Cuando no declara un ViewResolver , Spring registra un InternalResourceViewResolver predeterminado que crea instancias de JstlView para renderizar la View .

La clase JstlView extiende InternalResourceView que es

Wrapper para un JSP u otro recurso dentro de la misma aplicación web. Expone objetos de modelo como atributos de solicitud y reenvía la solicitud a la URL de recurso especificada utilizando javax.servlet.RequestDispatcher.

Se supone que una URL para esta vista especifica un recurso dentro de la aplicación web, adecuado para el método forward o include de RequestDispatcher.

Bold es mío. En otras palabras, la vista, antes de la representación, intentará obtener un RequestDispatcher al que forward() . Antes de hacer esto, comprueba lo siguiente

 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 es el nombre de la vista, lo que devolvió del @Controller . En este ejemplo, eso es preference . La variable uri contiene el uri de la solicitud que se maneja, que es /context/preference .

El código anterior se da cuenta de que si tuviera que reenviar a /context/preference , el mismo servlet (ya que el mismo manejaba el anterior) manejaría la solicitud y entraría en un ciclo sin fin.


Cuando declara un ThymeleafViewResolver y un ServletContextTemplateResolver con un prefix y suffix específicos, construye la View diferente, dándole una ruta como

 WEB-INF/web-templates/preference.html 

ThymeleafView instancias de ThymeleafView ubican el archivo relativo a la ruta ServletContext utilizando un ServletContextResourceResolver

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

que eventualmente

 return servletContext.getResourceAsStream(resourceName); 

Esto obtiene un recurso que es relativo a la ruta ServletContext . Luego puede usar TemplateEngine para generar el HTML. No hay forma de que un ciclo sin fin pueda suceder aquí.

Resolví este problema usando @ResponseBody como a continuación:

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

Así es como resolví este problema:

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

Aquí hay una solución fácil si no te importa renderizar la vista.

Cree una subclase de InternalResourceViewResolver que no compruebe las rutas de vista circular:

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

Luego configura tu prueba con ella:

 MockMvc mockMvc; @Before public void setUp() { final MyController controller = new MyController(); mockMvc = MockMvcBuilders.standaloneSetup(controller) .setViewResolvers(new StandaloneMvcTestViewResolver()) .build(); } 

@Controller@RestController

Tuve el mismo problema y noté que mi controlador también estaba anotado con @Controller . Reemplazarlo con @RestController resolvió el problema. Aquí está la explicación de Spring Web MVC :

@RestController es una anotación compuesta que está meta-anotada con @Controller y @ResponseBody que indica un controlador cuyos métodos heredan la anotación @ResponseBody de nivel de tipo y, por lo tanto, escribe directamente en el cuerpo de respuesta frente a la resolución y representación de la vista con una plantilla HTML.

Si está utilizando Spring Boot, agregue la dependencia de thymeleaf en su pom.xml:

   org.thymeleaf thymeleaf-spring4 2.1.6.RELEASE  

Para Thymeleaf:

Acabo de empezar a usar la spring 4 y la hoja de tomillo, cuando encontré este error, se resolvió añadiendo:

     

Estoy usando Spring Boot para intentar cargar una página web, no para probar, y tuve este problema. Mi solución fue un poco diferente a las anteriores considerando las circunstancias ligeramente diferentes. (Aunque esas respuestas me ayudaron a entenderlo.)

Simplemente tuve que cambiar mi dependencia de arranque Spring Boot en Maven desde:

  org.springframework.boot spring-boot-starter-web  

a:

  org.springframework.boot spring-boot-starter-thymeleaf  

Simplemente cambiando la ‘web’ a ‘thymeleaf’ me arregló el problema.

Al usar la anotación @Controller , necesita las anotaciones @RequestMapping y @ResponseBody . Vuelve a intentarlo luego de agregar la anotación @ResponseBody

Utilizo la anotación para configurar la aplicación web Spring, el problema se solucionó agregando un bean InternalResourceViewResolver a la configuración. Espero que sea útil.

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

Esto está sucediendo porque Spring está eliminando “preferencia” y añadiendo nuevamente la “preferencia” haciendo la misma ruta que la solicitud Uri.

Pasando así: solicite Uri: “/ preferencia”

eliminar “preferencia”: “/”

anexar ruta: “/” + “preferencia”

cadena final: “/ preferencia”

Esto se está metiendo en un ciclo que Spring te notifica lanzando una excepción.

Lo mejor es que le interese dar un nombre de vista diferente como “preferenceView” o cualquier cosa que desee.

Estoy usando Spring Boot con Thymeleaf. Esto es lo que funcionó para mí. Hay respuestas similares con JSP, pero tenga en cuenta que estoy usando HTML, no JSP, y que están en la carpeta src/main/resources/templates como en un proyecto estándar de Spring Boot, como se explica aquí . Este también podría ser tu caso.

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

Espero que esto ayude.

intente agregar comstackción (“org.springframework.boot: spring-boot-starter-thymeleaf”) dependencia a su archivo gradle.Thymeleaf ayuda a mapear vistas.

Otro enfoque simple:

 package org.yourpackagename; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.web.SpringBootServletInitializer; @SpringBootApplication public class Application extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(PreferenceController.class); } public static void main(String[] args) { SpringApplication.run(PreferenceController.class, args); } } 
    Intereting Posts