Spring MVC ContentNegotiationConfigurer

Este tutorial Spring MVC lo dedicaremos al estudio de la clase ContentNegotiationConfigurer esta le permitirá a nuestro controlador generar una vista en diversos formatos de salida, como: XML, JSON, PDF, XLS, CVS, etc., anteriormente hemos aprendido a generar estas vistas, ahora veremos como un mismo controlador puede ser usado para generar múltiples vistas, utilizando el mismo método de respuesta, a este proceso se le llama negociación de contenido.

Para configurar la negociación de contenido utilizaremos la anotación @EnableWebMvc y extenderemos la clase WebMvcConfigurerAdapter para facilitar el proceso de configuración, debemos sobre-escribir el método configureContentNegotiation() del siguiente modo:

@Configuration
@EnableWebMvc
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.
                favorPathExtension(true).
                favorParameter(true).
                ignoreAcceptHeader(true).
                parameterName("formato").
                defaultContentType(MediaType.APPLICATION_JSON).
                mediaType("xml", MediaType.APPLICATION_XML).
                mediaType("json", MediaType.APPLICATION_JSON);
    }

Spring utiliza tres formas para saber el tipo de contenido que debe generar:

  • Utilizando la extensión de la URL, ejemplo: /customer/data.json
  • A través de un parámetro en la URL, ejemplo: /customer/data?format=json
  • Con la cabecera HTTP usando Accept: application/json, por ejemplo.

Nuestro configuración utiliza las dos primeras, las activamos con los métodos: favorPathExtension(true) y favorParameter(true), el tercer modo no lo usaremos esto lo indicamos con: ignoreAcceptHeader(true), para indicar el parámetro que usaremos para indicar el tipo de salida que deseamos generar usamos: parameterName("formato"), el método defaultContentType() establece el contenido que se generará por defecto y por ultimo indicamos los tipos de contenido que soportará nuestra aplicación web.

Esta configuración genera la salida en formato JSON y XML usando la librería Jackson 2.x por lo que debemos agregar las dependencias: jackson-databind y jackson-dataformat-xml, editamos el POM y agregamos:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.8</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
    <version>2.6.3</version>
</dependency>

El controlador es el siguiente:

@Controller
public class CustomerController {

    @Autowired
    private CustomerService cs;

    @ResponseBody
    @RequestMapping(value = "/datos", method = RequestMethod.GET)
    public List<Customer> datos(Model model) {
        return cs.findAll();
    }
}

Con esto nuestro controlador será capaz de generar la respuesta en formato JSON o XML según se especifique en la URL, ya sea mediante la extensión o usando el parámetro indicado.

SpringMVC ContentNegotiationManager

Como vemos podemos usar la extensión de la URL para indicar el formato de salida o también el parámetro indicado, para nosotros formato, el controlador recibe la petición y rellena el modelo, luego el ContentNegotiationManager decide cual será la clase que usará para generar la salida dependiendo del formato especificado, el este ejemplo ambas salidas son creadas usando la librería Jackson 2.

Spring MVC ContentNegotiatingViewResolver

El componente ContentNegotiatingViewResolver implementa la interface ViewResolver por lo que podemos usarla para resolver las vistas basándonos en negociación de contenido, este componente no resuelve las vistas por si mismo, su trabajo es delegar esta función a otros ViewResolver, como los que vimos en tutoriales previos.

Crearemos un ContentNegotiatingViewResolver que nos permitirá manejar vistas: JSON, XML, PDF, HTML y XLS, para ello debemos agregar el siguiente bean a nuestra configuración.

@Bean
public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {

    MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
    jsonView.setPrettyPrint(true);

    MappingJackson2XmlView xmlView = new MappingJackson2XmlView();

    ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
    cnvr.setContentNegotiationManager(cnm);
    cnvr.setDefaultViews(Arrays.asList(jsonView, xmlView));

    return cnvr;
}

Las clases MappingJackson2JsonView y MappingJackson2XmlView son usadas para generar la salida en formato JSON y XML respectivamente, ambas son parte de la librería Jackson 2.x por lo que requerimos las dependencias que ya mencionamos en la primera sección de este tutorial.

Las clases PdfView y XlsView son usadas para generar las salidas en formato PDF y Excel XLS respectivamente, estas fueron creadas por nosotros en el tutorial Spring MVC Vistas PDF y XLS, estas serán resultas por el siguiente componente:

@Bean
public ViewResolver resourceViewResolver() {
    ResourceBundleViewResolver rbvr = new ResourceBundleViewResolver();
    rbvr.setBasename("views");
    rbvr.setOrder(1);
    return rbvr;
}

El ResourceBundleViewResolver requiere el siguiente archivo: views.properties.

customerx.pdf.(class)=carmelo.spring.view.PdfView
customerx.xls.(class)=carmelo.spring.view.XlsView

Para las vistas HTML usaremos el ya muy conocido InternalResourceViewResolver que generará las vistas a partir del archivo .jsp indicado por el nombre lógico devuelto por el controlador, para mas detalles puedes ver: Spring MVC Vistas JSP.

@Bean
public ViewResolver jspViewResolver() {
    InternalResourceViewResolver irvr = new InternalResourceViewResolver();
    irvr.setPrefix("/WEB-INF/views/");
    irvr.setSuffix(".jsp");
    irvr.setOrder(2);
    return irvr;
}

La clase de configuración quedará de este modo:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "carmelo.spring.controller")
public class WebAppConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.ignoreAcceptHeader(true);
        configurer.defaultContentType(MediaType.TEXT_HTML);
    }

    @Bean
    public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {

        MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
        jsonView.setPrettyPrint(true);

        MappingJackson2XmlView xmlView = new MappingJackson2XmlView();

        ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
        cnvr.setContentNegotiationManager(cnm);
        cnvr.setDefaultViews(Arrays.asList(jsonView, xmlView));

        return cnvr;
    }

    @Bean
    public ViewResolver resourceViewResolver() {
        ResourceBundleViewResolver rbvr = new ResourceBundleViewResolver();
        rbvr.setBasename("views");
        rbvr.setOrder(1);
        return rbvr;
    }

    @Bean
    public ViewResolver jspViewResolver() {
        InternalResourceViewResolver irvr = new InternalResourceViewResolver();
        irvr.setPrefix("/WEB-INF/views/");
        irvr.setSuffix(".jsp");
        irvr.setOrder(2);
        return irvr;
    }
}

Nuestro controlador será usado para generar diferentes vistas, pero el mismo se mantiene simple, solo necesitaremos un método que solo se encargará de rellenar los datos del modelo, será el ContentNegotiatingViewResolver quién decidirá que vista se a a generar dependiendo de la extensión de la URL.

@Controller
public class CustomerController {

    @Autowired
    private CustomerService cs;

    @RequestMapping(value = "/customer", method = RequestMethod.GET)
    public String customer(Model model) {
        model.addAttribute("customerList", cs.findAll());
        return "customer";
    }

}

Probando lo que tenemos en el navegador:

Spring MVC ContentNegotiatingViewResolver JSON

Spring MVC ContentNegotiatingViewResolver PDF

Spring MVC ContentNegotiatingViewResolver HTML

Observa que la URL solo cambia la extensión, dependiendo de ella se genera la vista adecuada, en la última imagen la URL no tiene extensión esto se debe a que establecimos que la vista HTML es la aplicada por defecto.

Descargar código: Spring MVC ContentNegotiationViewResolver.zip

Comentarios

Entradas populares de este blog

Conectar SQL Server con Java

Gauss Seidel y Jacobi

Entrenar OpenCV en Detección de Objetos

Procesamiento de imágenes en OpenCV

Acceso a la webcam con OpenCV