4.3 Simplificación del JDBC
5. Resolver vistas
5.3 Redireccionamiento de vistas
Un controlador normalmente devuelve un nombre de vista lógico que una resolución de vista convierte a una tecnología de vista particular. Para tecnologías de vistas como JSP que se procesan a través del Servlet o del motor JSP, esta resolución suele ser manejada a través de la combinación de InternalResourceViewResolver y InternalResourceView, que usa el método RequestDispatcher.forward(..) de la API Servlet o el método RequestDispatcher.include(). Para otras tecnologías de vista, tales como la Velocity, XSLT, etc., la vista en sí escribe su contenido directamente en la secuencia de respuesta.
RedirectView
Una forma de forzar una redirección como el resultado de una respuesta del controlador es crear y devolver una instancia de RedirectView. En este caso, DispatcherServlet no utiliza el mecanismo normal de resolución de vistas. Por el contrario, ya que se ha redirigido la vista el DispatcherServlet simplemente instruye a la vista para hacer su trabajo.
RedirectView emite una llamada a HttpServletResponse.sendRedirect() que devuelve al navegador del cliente una redirección HTTP. Por defecto todos los atributos del modelo son considerados como variables de la plantilla URI en la URL de redirección. De los atributos restantes, los que son tipos primitivos o colecciones/arrays de tipos primitivos se adjuntan automáticamente como parámetros de consulta.
Anexar atributos de tipo primitivo como parámetros de consulta puede ser beneficioso si una instancia del modelo está específicamente preparada para la redirección. Sin embargo, los controladores del modelo pueden contener atributos adicionales añadidos para fines de representación. Para evitar la posibilidad de que tales atributos aparezcan en la URL un controlador puede declarar un argumento como tipo RedirectAttributes y utilizarlo para especificar los atributos exactos que se ponen a disposición de RedirectView. Si el método del controlador decide hacer la redirección, se utiliza el contenido de RedirectAttributes, de lo contrario, se utiliza el contenido del modelo.
Algo a tener en cuenta es que las variables de la plantilla URI de las peticiones se hacen automáticamente cuando se expande una URL de redirección, por lo que no es necesario añadirlas explícitamente ni mediante Model ni por RedirectAttributes. Por ejemplo:
@RequestMapping(value = "/files/{path}", method = RequestMethod.POST)
107
// ...
return "redirect:files/{path}"; }
5.4 ContentNegotiatingViewResolver
ContentNegotiatingViewResolver no resuelve vistas por sí mismo, sino que delega esta función a otros resolvers, seleccionando la vista que más se asemeja a la representación solicitada por el cliente. Existen dos estrategias para solicitar una representación del servidor:
Utilizar un URI distintivo para cada recurso, por lo general mediante el uso de una extensión de archivo diferente en el URI.
Utilizar el mismo URI que el cliente para localizar el recurso, pero estableciendo la aceptación del encabezado de la solicitud HTTP para enumerar los tipos media. Para soportar múltiples representaciones de un recurso, Spring ofrece el ContentNegotiatingViewResolver para resolver una vista basada en la extensión de un archivo o aceptar el encabezado de una solicitud HTTP. ContentNegotiatingViewResolver no realiza la resolución de la vista, pero en vez eso delega a una lista de resolvers que se especifica a través de la propiedad ViewResolvers de los beans.
ContentNegotiatingViewResolver selecciona una vista apropiada para atender la solicitud comparando el tipo media de la petición con el tipo media soportado por la vista asociada a cada uno de sus ViewResolvers. La primera vista de la lista que tiene un Content-Type compatible devuelve la representación del cliente. Si una vista compatible no puede ser suministrada por la cadena de ViewResolvers, entonces se consulta la lista de vistas específicas a través de la propiedad DefaultViews.
He aquí un ejemplo de configuración de un ContentNegotiatingViewResolver:
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewReso lver">
<property name="mediaTypes">
<map>
<entry key="atom" value="application/atom+xml"/>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="viewResolvers">
<list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolv er">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
108
</property>
<property name="defaultViews">
<list> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonVi ew" /> </list> </property> </bean> <bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/>
El InternalResourceViewResolver se encarga de la traducción de los nombres de vista y las páginas JSP, mientras que el BeanNameViewResolver devuelve una vista basada en el nombre de un bean. En este ejemplo, el content del bean es una clase que hereda de AbstractAtomFeedVie, que devuelve un Atom RSS.
6. Construcción URI
Spring MVC proporciona un mecanismo para la construcción y la codificación de un URI utilizando UriComponentsBuilder y UriComponents.
Por ejemplo, para ampliar y codificar una cadena de plantilla URI:
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://example.com/hotels/{hotel}/bookings/{booking}").build(); URI uri = uriComponents.expand("42", "21").encode().toUri();
Hay que tener en cuenta que UriComponents es inmutable y las operaciones expand() y encode() devuelven nuevas instancias si es necesario.
También se puede ampliar y codificar utilizando componentes individuales URI:
UriComponents uriComponents = UriComponentsBuilder.newInstance()
.scheme("http").host("example.com").path("/hotels/{hotel}/bookings/{bo oking}").build()
.expand("42", "21") .encode();
En un entorno Servlet la sub-clase ServletUriComponentsBuilder proporciona métodos generadores estáticos para copiar la información URL disponible desde las peticiones Servlet:
109
// Re-use host, scheme, port, path and query string // Replace the "accountId" query param
ServletUriComponentsBuilder ucb =
ServletUriComponentsBuilder.fromRequest(request)
.replaceQueryParam("accountId", "{id}").build() .expand("123")
.encode();
Alternativamente, se puede optar por copiar un subconjunto de los datos disponibles incluyendo la ruta de contexto:
// Re-use host, port and context path // Append "/accounts" to the path
ServletUriComponentsBuilder ucb =
ServletUriComponentsBuilder.fromContextPath(request) .path("/accounts").build()
O en caso de que el DispatcherServlet este mapeado por su nombre (por ejemplo, /main/*), también se puede tener incluido la parte literal en el mapeo del Servlet:
// Re-use host, port, context path
// Append the literal part of the servlet mapping to the path // Append "/accounts" to the path
ServletUriComponentsBuilder ucb =
ServletUriComponentsBuilder.fromServletMapping(request) .path("/accounts").build()