viernes, 19 de febrero de 2016

Spring MVC - Parte 2: Selección de manejadores de peticiones

En el tutorial anterior aprendimos cómo configurar nuestra aplicación para hacer uso de Spring MVC, además creamos dos Controllers con un método cada uno para manejar las peticiones recibidas por el usuario. En esa ocasión configuramos los métodos de una manera simple y directa para responder a las peticiones, sin embargo, Spring MVC permite varias formas de indicar a qué peticiones puede responder un manejador particular.

En este tutorial aprenderemos las distintas maneras que tenemos para indicar a qué peticiones debe responder un método dentro de un Controller, y cómo podemos tomar ventaja de estas formas para recibir distintos tipos de información desde nuestras páginas web.

En este tutorial nos enfocaremos únicamente a cómo podemos configurar la anotación "@RequestMapping" y dejaremos para otro tutorial para las distintas maneras que tenemos para recibir parámetros en los métodos de manejo de peticiones.

El componente encargado de decidir a qué manejador debe ser dirigida una petición es el "RequestMappingHandlerMappings". Podemos pensar en los métodos de los controladores como una colección de endpoints únicos con mapeos para cada método, que se derivan de la información que colocamos en las anotaciones "@RequestMapping", tanto a nivel de clase como a nivel de método.

Comencemos creando un nuevo proyecto en NetBeans. Para esto vamos al Menú "File->New Project..." En la ventana que se abre seleccionamos la categoría "Java Web" y en el tipo de proyecto "Web Application".

Le damos una ubicación y un nombre al proyecto, en mi caso será "SpringMVCXMapeoPeticiones". Hacemos clic en el botón "Next" y se nos preguntará en qué servidor queremos desplegar nuestra aplicación, en mi caso usaré Tomcat 8 (pero debe funcionar igual en cualquier servidor de aplicaciones Java). También cambiaré el context path para tener algo más claro, en mi caso pondré "/peticiones". Presionamos el botón "Finish" y veremos aparecer en el editor nuestra página "index.html"

Agregamos la biblioteca de "SpringMVC4.2" que creamos en el primer tutorial de la serie de Spring MVC, para esto hacemos clic derecho sobre el nodo "Libraries" del proyecto, en el menú que aparece elegimos la opción "Add Library..." y en la ventana que se abre seleccionamos la biblioteca "SpringMVC4.2".

Creamos un nuevo paquete en el nodo "Source Packages" del proyecto, que en este caso será "com.javatutoriales.springmvc.peticiones".



Para este proyecto usaré configuración en Java, si quieren ver con más detalle cómo hacer esto o quieren ver cómo hacer lo mismo con archivos de configuración en XML pueden revisar el tutorial anterior, de todas formas al final del tutorial colocaré el código de ambas versiones para que puedan descargarlo e iré explicando las equivalencias entre en código Java y el XML.

Dentro del paquete que acabamos de crear creamos otros dos paquetes, el primero será "config", para poner el código de configuración de la aplicación, y el segundo será "controllers" para poner... pues los Controllers de la aplicación.

Dentro del paquete "config" creamos las clases "SpringWebConfig" y "SpringWebApplicationInitializer" que indicamos en el tutorial anterior. Dejaré el código de ambas clases aquí; si quieren una explicación detallada de cómo funciona cada una pueden revisarlo en el tutorial anterior.

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.javatutoriales.springmvc.peticiones.controllers")
public class SpringWebConfig {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");
        
        return viewResolver;
    }
}


public class SpringWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(SpringWebConfig.class);
        context.setServletContext(servletContext);

        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("spring-web", new DispatcherServlet(context));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}

Para el caso de los que realicen el tutorial con archivos de configuración en XML, será necesario agregar dos archivos, el "web.xml" y el archivo "springMVCconfig.xml" dentro del directorio "WEB-INF" y "/WEB-INF/config/" respectivamente.

El archivo "web.xml" queda de la siguiente forma:

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">
    
    <servlet>
        <servlet-name>spring-web</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/config/springMVCconfig.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring-web</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
</web-app>


Y el archivo "springMVCconfig.xml" queda de la siguiente forma:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:component-scan base-package="com.javatutoriales.springmvc.peticiones.controllers" />
    <mvc:annotation-driven  />
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
</beans>

Ahora, dentro del paquete "controllers" creamos una clase llamada "PeticionesController". De momento a esta clase sólo le colocaremos la anotación "@Controller":

@Controller
public class PeticionesController {

}

La base de lo que veremos en este tutorial es la anotación "@RequestMapping", esta anotación nos ayuda a indicar a qué peticiones responderá un método determinado.

Ya en el tutorial anterior vimos un poco de este tema y decíamos que para esto se usa una combinación de los valores que tenemos en el context path (que definimos como "peticiones"), el patrón de URLs a las que responde el DispatcherServlet ("/"), el valor de la anotación "@RequestMapping" de la clase y el valor de la anotación "@RequestMapping" del método.

Los dos primeros valores ya los tenemos, pero ahora veremos cómo manejar los dos últimos.

Colocamos la anotación "@RequestMapping" a nivel de la clase, y dentro de esta colocamos como valor "tutorial". Eso quiere decir que la clase responderá a cualquier petición que llegue para nuestra aplicación e inmediatamente después del nombre del contexto tenga "/tutorial", como por ejemplo:

http://localhost:8080/peticiones/tutorial/saludo

o

http://localhost:8080/peticiones/tutorial/metodo 

(claro, siempre y cuando tengamos métodos que mapeen a estos valores). Hasta ahora nuestra clase se ve así:

@Controller
@RequestMapping("tutorial")
public class PeticionesController {

}

Ahora agregaremos el primer método, el cual llamaremos "directo", este método regresará un objeto "ModelAndView" y no recibirá ningún parámetro:

public ModelAndView directo() {

}

Pondremos una anotación "@RequestMapping" en este método, con la que veremos la primera forma de indicar a qué peticiones responderá el método, que es indicar de forma directa la URL de las peticiones, esto sin usar ningún patrón o expresión de ningún tipo. Colocamos como valor de nuestra anotación "directo", de esta forma:

@RequestMapping("directo")
public ModelAndView directo() {

}

Esto quiere decir que este método será quien maneje las peticiones que lleguen a la dirección http://localhost:8080/peticiones/tutorial/directo.

Como vemos, en esta forma indicamos la URL sin mayor "adorno". Aquí podemos definir cualquier URL que necesitemos, por ejemplo podría ser "/modulo/funcionalidad/metodo", de la siguiente forma:

@RequestMapping("/modulo/funcionalidad/metodo")

Ahora completemos el cuerpo del método. La vista que regresaremos se llamará "contenido", y le pasaremos un atributo al modelo que se llamará "metodo" y que nos ayudará a saber qué método fue el que se invocó. Colocaremos como valor de este atributo "directo". Nuestro método directo queda de la siguiente forma:

@RequestMapping("directo")
public ModelAndView directo() {
    return new ModelAndView("contenido", "metodo", "directo");
}

Ahora creamos un directorio dentro de "WEB-INF" que se llame "pages", y dentro de este una jsp llamada "contenido.jsp":



El contenido de esta página mostrará el valor del atributo "metodo" que estamos pasando al modelo, y queda de la siguiente forma:

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JavaTutoriales.com | Spring MVC 2</title>
    </head>
    <body>
        <h1>El método llamado fue:</h1>
        <h2>${metodo}</h2>
    </body>
</html>

Si ejecutamos nuestra aplicación y entramos a la siguiente dirección:

http://localhost:8080/peticiones/tutorial/directo

Debemos ver la siguiente página.



Con lo que comprobamos que el método que se llamó efectivamente fue "directo".

Spring MVC también nos permite recibir variables a través de una URL, e indicar que un método debe manejar las peticiones que contienen estas variables. Estas variables son llamadas path variables, y aunque estas caen dentro de varios temas de Spring MVC las veremos de forma breve aquí y las abordaremos más a detalle en tutoriales posteriores.

A estas variables podemos darles un nombre que luego usaremos para hacer referencia a ella y para recibir el valor de esa variable como un argumento de nuestro método.Las path variables se indican colocándolas entre llaves o curly brackets ({ y }).

Agreguemos un nuevo método que se llame "pathVariables" que regrese un objeto "ModelAndView" y de momento no recibirá parámetros:

public ModelAndView pathVariables() {

}

Anotaremos este método con "@RequestMapping" para convertirlo en un manejador de peticiones. Como valor para esta anotación indicaremos una parte estática y una variable que será el "id" de un usuario ficticio; recuerden que esta variable debemos colocarla entre llaves:

@RequestMapping("/usuario/{idUsuario}")
public ModelAndView pathVariables() {        

}

Dependiendo del tipo de dato que definamos que sea "idUsuario" este método podrá manejar peticiones que se hagan a "http://localhost:8080/peticiones/tutorial/usuario/1", "http://localhost:8080/peticiones/tutorial/usuario/alex", etc. Si definimos "idUsuario" como un tipo entero (int o long) sólo podrá manejar el primer tipo de petición; si lo definimos como String podrá manejar tanto la primera como la segunda petición.

Para indicar el parámetro que recibirá el valor de esta variable usamos la anotación "@PathVariable" en el argumento del método "pathVariables" que recibirá el valor de esta variable, de la siguiente forma:

@RequestMapping("/usuario/{idUsuario}")
public ModelAndView pathVariables(@PathVariable String idUsuario) {

}

Esto quiere decir que el valor de la variable "idUsuario" quedará en la cadena que tiene este mismo nombre.
Para terminar el método regresemos un objeto de tipo "ModelAndView" que nos envíe a la vista "contenido" y que coloque "pathVariables" como valor del atributo "metodo", de esta forma:

@RequestMapping("/usuario/{idUsuario}")
public ModelAndView pathVariables(@PathVariable String idUsuario) {
    return new ModelAndView("contenido", "metodo", "pathVariables");
}

Hacemos una petición a la siguiente URL:

http://localhost:8080/peticiones/tutorial/usuario/alex

Con lo que debemos ver la siguiente página:



Podemos poner tantas path variables como necesitemos; por ejemplo: si modificamos el valor de "@RequestMapping" para que quede de la siguiente forma (y agregamos su variable correspondiente):


@RequestMapping("/usuario/{idUsuario}/tutorial/{idTutorial}")
public ModelAndView pathVariables(@PathVariable String idUsuario, @PathVariable String idTutorial) {
    return new ModelAndView("contenido", "metodo", "pathVariables");
}

Si ahora hacemos una petición a la siguiente URL:

http://localhost:8080/peticiones/tutorial/usuario/alex/tutorial/springmvc

Veremos la misma pantalla que en el caso anterior:



La recomendación es colocar siempre las path variables en su propio segmento de URL (entre diagonales o slashs "/") para que estas sean más claras y tener un mejor manejo de las mismas, aunque en realidad nada nos impide hacer algo como esto:

@RequestMapping("/usuario/{idUsuario}/tutorial{idTutorial}")

Fíjense que hemos eliminado la diagonal que separa "tutorial" de la variable "idTutoria" por lo que este método ahora puede manejar peticiones como la siguiente:

http://localhost:8080/peticiones/tutorial/usuario/alex/tutorialspringmvc

La tercera forma que tenemos para definir qué peticiones manejará un método es haciendo uso de algo llamado URIs templates. Los templates de URIs son usados para definir el patrón de peticiones que serán atendidas por un manejador particular.

Los URI templates so expresiones regulares que colocamos como valor de la anotación "@RequestMapping". La sintaxis de estas expresiones es parecida a la que acabamos de ver para los URI templates: "{varName:regex}" , y también podemos colocar tantas como queramos.

Como podemos ver en la sintaxis, cuando los URI usan expresiones regulares, están compuestos por dos partes, la primera es el nombre de la variable que se usará para colocar el valor que se recibe en la URL y que representa a esa expresión, y la segunda es la expresión regular.

Agreguemos un nuevo método llamado "template" que no reciba parámetros y que regrese un objeto tipo "ModelAndView":

public ModelAndView template() {

}

Indicaremos que este método es un manejador de peticiones con la anotación "@RequestMapping", y como valor de esta anotación colocaremos un template que nos permita recibir el nombre de un archivo que esté conformado por tres partes: el nombre, el número de versión y la extensión, esto podemos hacerlo de la siguiente forma:

@RequestMapping("{archivo:[a-zA-Z]+-[\\d\\.]+\\.[a-z]+}")
public ModelAndView template() {

}

En donde "archivo" es el nombre de la variable en la que quedará el valor que coloquemos en la URI, y "[a-z-]+-[\\d\\.]+\\.[a-z]+" es la expresión.

La expresión anterior permite que este método maneje peticiones a la URI "archivo-1.0.0.0.txt" o "imagen-1.2.jpg" o "elMejorTutorialDeSpringMVC-1.0.0.html".

Como esta es una especie de modificación de las path variables, podemos obtener el valor de la varable "archivo" de la misma forma que lo hicimos antes, usando la anotación "@PathVariable", de la siguiente forma:

@RequestMapping("{archivo:[a-zA-Z]+-[\\d\\.]+\\.[a-z]+}")
public ModelAndView template(@PathVariable String archivo) {

}

Para terminar nuestro método regresemos un objeto "ModelAndView" que nos envíe a la vista "contenido" y coloque el atributo "metodo" con el valor de "template", por lo que nuestro método queda de la siguiente forma:

@RequestMapping("{archivo:[a-zA-Z]+-[\\d\\.]+\\.[a-z]+}") 
public ModelAndView template() {
    return new ModelAndView("contenido", "metodo", "template");
}

Ahora, hagamos una petición a:

http://localhost:8080/peticiones/tutorial/pagina-1.2.3.html

Con lo que debemos ver la siguiente página:



Esto comprueba que el método "template" fue llamado con nuestra petición.

Al igual que en al caso de las path variables, podemos indicar tantos URI templates como necesitemos, en este caso podríamos tomar ventaja de esto para obtener cada una de las partes que conforman el nombre del archivo anterior, de la siguiente forma:

@RequestMapping("{nombre:[a-zA-Z]+}-{version:[\\d\\.]+}{extension:\\.[a-z]+}")
public ModelAndView template(@PathVariable String nombre, @PathVariable String version, @PathVariable String extension) {
    return new ModelAndView("contenido", "metodo", "template");
}

Y así evitarnos el estar parseando o separando posteriormente cada parte de forma individual.

Cuando tenemos varios métodos que pueden ser válidos para manejar la misma petición, Spring MVC siempre elije el más específico. En el ejemplo anterior definimos nuestro patrón de la siguiente forma:

{nombre:[a-zA-Z]+}-{version:[\\d\\.]+}{extension:\\.[a-z]+}

Si definiéramos un patrón más específico para un tipo de peticiones en el que, por decir, indicáramos el nombre, la versión y la extensión de la página, de la siguiente forma:

pagina-1.2.3.html

Cuando hagamos una petición para la siguiente URL:

http://localhost:8080/peticiones/tutorial/pagina-1.2.3.html

Esta sería manejada por el método que defina este patrón, mientras que el resto de las peticiones seguirá siendo manejado por el método anterior. Veamos esto en código. Agreguemos un método aún más específico cuyo patrón sea "pagina-1.2.3.html", de la siguiente forma:

@RequestMapping("pagina-1.2.3.html")
public ModelAndView templateMuchoMasEspecifico() {
    return new ModelAndView("contenido", "metodo", "templateMuchoMasEspecifico");
}

Si hacemos nuevamente una petición a la URL anterior, veremos la siguiente página:



Para determinar cuál de los métodos anteriores llamar, Spring MVC usa algo llamado "Path Pattern Comparison", el cual indica que cuando una URL coincide con múltiples patrones, se usa un ordenamiento para encontrar el patrón más específico.

Un patrón con un conteo bajo de variables de URI y comodines es considerado más específico. Por ejemplo "/cursos/{curso}/*" tiene 1 variable URI y 1 comodín y es considerado más específico que "/cursos/{curso}/**" que tiene 1 variable URI y 2 comodines.

Veamos un ejemplo, agregaremos un nuevo método con este último patrón, el nombre del método será "dosComodines" y tiene el siguiente método:

@RequestMapping("/curso/{curso}/**")
public ModelAndView dosComodines(@PathVariable String curso) {
    return new ModelAndView("contenido", "metodo", "dosComodines");
}

Si ingresamos a la siguiente URL:

http://localhost:8080/peticiones/tutorial/curso/spring/abc

Veremos la siguiente pantalla:



Si ahora agregamos un nuevo método con un patrón mucho más específico, en este caso "/curso/{curso}/*", con el siguiente código:

@RequestMapping("/curso/{curso}/*")
public ModelAndView unComodin(@PathVariable String curso) {
    return new ModelAndView("contenido", "metodo", "unComodin");
}

Y entramos a la misma dirección de hace un momento:

http://localhost:8080/peticiones/tutorial/curso/spring/abc

Ahora veremos la siguiente pantalla:



Como vemos, el método que se invoca es el que es más específico a esta petición, ya que incluye el nombre del curso, en este caso "spring", y una ruta adicional. "dosComodines" continuará invocándose con cualquier otra ruta como las siguientes:

http://localhost:8080/peticiones/tutorial/curso/spring/abc/123
http://localhost:8080/peticiones/tutorial/curso/spring
http://localhost:8080/peticiones/tutorial/curso/hibernate

Cuando dos patrones tienen el mismo conteo, el que es más largo se considera más específico. Por ejemplo "/javatutoriales/spring*" es más largo y por lo tanto más específico que "/javatutoriales/*".

Por ejemplo, si agregamos los siguientes métodos:

@RequestMapping("/javatutoriales/*")
public ModelAndView masCorto() {
    return new ModelAndView("contenido", "metodo", "masCorto");
}

@RequestMapping("/javatutoriales/spring*")
public ModelAndView masLargo() {
    return new ModelAndView("contenido", "metodo", "masLargo");
}

Cuando entremos a la dirección:

http://localhost:8080/peticiones/tutorial/javatutoriales/hibernate

Veremos la pantalla:



Que indica que el método llamado fue "masCorto", mientras que si hacemos una petición a esta URL:

http://localhost:8080/peticiones/tutorial/javatutoriales/springmvc

Veremos la pantalla:



Que indica que el método llamado fue "masLargo".

Cuando dos patrones tienen el mismo conteo y longitud, el patrón con menos comodines es considerado más específico. Por ejemplo "/tutoriales/{tutorial}" es más específico que "/tutoriales/*".

Como ejercicio al lector, si agregamos un método con el patrón "/javatutoriales/{tutorial}" y entramos a la siguiente dirección:

http://localhost:8080/peticiones/tutorial/javatutoriales/hibernate

¿Qué método será llamado?

Y si entramos a:

http://localhost:8080/peticiones/tutorial/javatutoriales/springmvc

¿Qué método será llamado?

Cuando tengan su respuesta, pueden dejarla en los comentarios ^_^.

También existen unas reglas especiales adicionales:
  • El patrón de mapeo por default, también conocido como "captura todo" ("/**"), es menos específico que cualquier otro patrón.
  • Un patrón con un prefijo, como "/publico/**" es menos específico que cualquier otro patrón que no contenga doble comodín. Por ejemplo "/publico/cursos/{a}/{b}/{c}" es más específico.

Otra manera que tenemos para determinar qué método responderá una petición, es por medio del método HTTP que indiquemos que manejará esa petición. @RequestMapping mapea por default a todos los métodos HTTP, sin embargo, podemos indicar exactamente a qué método o métodos debe responder usando su atributo "method".

"method" recibe como valor un elemento de la enumeración "org.springframework.web.bind.annotation.RequestMethod", la cual tiene una entrada por cada uno de los métodos HTTP que existen, o sea:
  • GET
  • HEAD
  • POST
  • PUT
  • PATCH
  • DELETE
  • OPTIONS
  • TRACE

Cuando en "@RequestMapping" agregamos el atributo "method", el valor del patrón de URL a la que responderá el método se debe colocar en el atributo "value".

Para hacer las siguientes pruebas será necesario hacer un par de llamadas que solamente con HTML es un poco complicado. Para facilitar un poco las cosas haremos un par de funciones con jQuery, para lo cual descargaremos la última versión de jQuery 1, que en este momento es 1.12.0. No usaremos jQuery 2 ya que no soporta las versiones 6, 7 y 8 de Internet Explorer (aunque no creo que ninguno de los lectores siga usándolos).

Vamos a la página de descarga de jQuery y bajamos la última versión de producción de jQuery (la versión comprimida) y la colocamos dentro de un directorio "js" dentro de las páginas web de la aplicación:



Regresaremos a la clase "PeticionesController" y agregaremos cuatro métodos. Los cuatro tendrán como patrón de peticiones "/metodo", solo que cada uno responderá a un método HTTP distinto, para no usar todos los métodos nos limitaremos a los siguientes: GET, POST, PUT, DELETE. Las anotaciones de estos métodos quedan de la siguiente forma:

@RequestMapping(value = "/metodo", method = RequestMethod.GET)

@RequestMapping(value = "/metodo", method = RequestMethod.POST)

@RequestMapping(value = "/metodo", method = RequestMethod.PUT)

@RequestMapping(value = "/metodo", method = RequestMethod.DELETE)

En el caso de los métodos "GET" y "POST" podemos manejarlos como lo hemos hecho hasta el momento, sin embargo en el caso de los métodos "PUT" y "DELETE", estos están bloqueados por default en Tomcat (y en otros servidores también). Existen dos formas de hacer que estos métodos sean llamados de forma correcta. La primera es habilitarlos en el servidor, sin embargo no usaremos esta ya que depende de cada servidor y la versión del mismo (aunque si obtienen un mensaje como "HTTP 405 - JSPs only permit GET POST or HEAD", lo siento no hay nada que hacer... excepto usar el segundo método).

La segunda forma (más genérica) es simplemente indicar a Spring MVC que convierta el valor de retorno del método a otro content-type (o mime-type), en este caso a texto plano, y que lo escriba directamente en el flujo de la respuesta; para esto usamos la anotación "@ResponseBody" y colocamos el tipo de retorno del método como "String". Como respuesta regresaremos el nombre del método que estamos invocando.

Los métodos quedan de la siguiente forma:

@RequestMapping(value = "/metodo", method = RequestMethod.GET)
public ModelAndView peticionGet() {
    return new ModelAndView("contenido", "metodo", "peticionGet");
}

@RequestMapping(value = "/metodo", method = RequestMethod.POST)
public ModelAndView peticionPost() {
    return new ModelAndView("contenido", "metodo", "peticionPost");
}

@RequestMapping(value = "/metodo", method = RequestMethod.PUT)
@ResponseBody
public String peticionPut() {
    return "peticionPut";
}

@RequestMapping(value = "/metodo", method = RequestMethod.DELETE)
@ResponseBody
public String peticionDelete() {
    return "peticionDelete";
}

Por ahora hemos terminado con el controlador. A continuación, modificaremos la página "index.html" para incluir, en primer lugar, la etiqueta que indica que se hará uso de jQuery.

<script src="js/jquery-1.12.0.min.js"></script>

Agregaremos una etiqueta "div", cuyo id será "contenido", y lo usaremos posteriormente para mostrar el resultado de la página que se regrese como respuesta a la petición. Esto nos dirá qué método estamos llamando.

<div id="contenido"></div>

Además, pondremos un botón por cada uno de los métodos HTTP que usaremos para la llamada:

<button id="botonGet">GET</button><br />
<button id="botonPost">POST</button><br />
<button id="botonPut">PUT</button><br />
<button id="botonDelete">DELETE</button><br />

Ahora, agregamos un script con una función de inicio de jQuery:

<script>
    
    $(function () {
    
    });
    
</script>

Agregaremos una función auxiliar, con nombre "llamadaAjax", que nos permita de forma sencilla hacer una llamada ajax a nuestro controlador. En este caso usaremos la función "$.ajax" de jQuery. El único parámetro que variará en este caso es el método HTTP que se usará en la petición. Por el momento el script se ve de la siguiente forma:

<script>

    $(function () {
    
    });

    function llamadaAjax(metodo) {
        $.ajax({
            url: 'tutorial/metodo',
            type: metodo,
            dataType: "text"
        });
    }
</script>

En caso de que la llamada sea exitosa se mostrará la página resultado en el div "contenido"; en caso de algún error se mostrará un mensaje que indique cuál es el código de error y la descripción del mismo, de la siguiente forma:

<script>

    $(function () {
    
    });

    function llamadaAjax(metodo) {
        $.ajax({
            url: 'tutorial/metodo',
            type: metodo,
            dataType: "text",
            success: function (data)
            {
                $('#contenido').html(data);
            },
            error: function (xhr, ajaxOptions, thrownError) {
                $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
            }
        });
    }
</script>

Para terminar con el script, dentro de la función de inicialización de jQuery agregamos un manejador del método "click" para cada uno de los botones. En este manejador lo único que se hará será una llamada al método "llamadaAjax", pasándole como parámetro el nombre del método HTTP que se usará para la llamada:

$(function () {
    $('#botonGet').click(function (e)
    {
        llamadaAjax('GET');
    });
    
    $('#botonPost').click(function (e)
    {
        llamadaAjax('POST');
    });
    
    $('#botonPut').click(function (e)
    {
        llamadaAjax('PUT');
    });
    
    $('#botonDelete').click(function (e)
    {
        llamadaAjax('DELETE');
    });
});

El script completo queda de la siguiente forma:

<script>

    $(function () {
    
        $('#botonGet').click(function (e)
        {
            llamadaAjax('GET');
        });
        
        $('#botonPost').click(function (e)
        {
            llamadaAjax('POST');
        });
        
        $('#botonPut').click(function (e)
        {
            llamadaAjax('PUT');
        });
        
        $('#botonDelete').click(function (e)
        {
            llamadaAjax('DELETE');
        });
    });

    function llamadaAjax(metodo) {
        $.ajax({
            url: 'tutorial/metodo',
            type: metodo,
            dataType: "text",
            success: function (data)
            {
                $('#contenido').html(data);
            },
            error: function (xhr, ajaxOptions, thrownError) {
                $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
            }
        });
    }
    
</script>

Y la página completa así:

<!DOCTYPE html>
<html>
    <head>
        <title>JavaTutoriales.com | Spring MVC</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script src="js/jquery-1.12.0.min.js"></script>
    </head>
    <body>

        <button id="botonGet">GET</button><br />
        <button id="botonPost">POST</button><br />
        <button id="botonPut">PUT</button><br />
        <button id="botonDelete">DELETE</button><br />
        
        <div id="contenido"></div>
        
        <script>
            $(function () {
                $('#botonGet').click(function (e)
                {
                    llamadaAjax('GET');
                });
                $('#botonPost').click(function (e)
                {
                    llamadaAjax('POST');
                });
                $('#botonPut').click(function (e)
                {
                    llamadaAjax('PUT');
                });
                $('#botonDelete').click(function (e)
                {
                    llamadaAjax('DELETE');
                });
            });

            function llamadaAjax(metodo) {
                $.ajax({
                    url: 'tutorial/metodo',
                    type: metodo,
                    dataType: "text",
                    success: function (data)
                    {
                        $('#contenido').html(data);
                    },
                    error: function (xhr, ajaxOptions, thrownError) {
                        $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
                    }
                });
            }
        </script>
    </body>
</html>

Si ejecutamos ahora nuestra aplicación veremos… una página de error mostrando un error "404":



Esto ocurre ya que no tenemos mapeado un controlador para este tipo de peticiones. Los usuarios más avanzados tal vez se estén preguntando "¿Qué no se supone que por default deberíamos ver la página index.html? Esto es una funcionalidad básica de los Servlets". La respuesta a esto es "Sí y no". "", en cuanto a que esto debería de ocurrir: Si Spring MVC no tiene mapeado un manejador para una petición esta debería delegar la misma petición al mecanismo default de Servlets. Y "No", en cuanto a que definimos que todas las peticiones pasarán por el Dispatcher Servlet, y no hemos definido que en otros casos Spring MVC delegará la petición al motor de Servlets.

¿Cómo hacemos esto? Pues bien, en el caso de Tomcat (y la mayoría de los motores de Servlets y JSPs de JEE) tienen un Servlet que por default maneja algunas de las peticiones que se hacen a la aplicación. En este caso particular la funcionalidad que nos interesa es la de servir contenido estático, en este caso la página "index.html"; sin embargo, por la forma en la que estamos configurando la aplicación, este Servlet por default JAMAS es llamado.

Nota: Si quieren ver un poco más de información pueden revisar la referencia del Default Servlet de Tomcat, también la documentación de la clase "WebApplicationInitializer" nos será de utilidad.


¿Cómo podemos cambiar esto? Pues bien, esto podemos hacerlo de forma programática en la clase "SpringWebConfig", que es la que estamos usando para configurar el framework. Para poder delegar las peticiones que el framework no entienda al Servlet por default del servidor, lo primero que debemos hacer es modificar nuestra clase "SpringWebConfig" para que extienda de la clase "WebMvcConfigurerAdapter". Esta clase tiene un método llamado "configureDefaultServletHandling", que recibe un objeto de tipo "DefaultServletHandlerConfigurer", que nos ayuda a configurar habilitar y configurar este Servlet default, en este caso para habilitarlo sólo debemos invocar el método "enable()" de esta instancia, de la siguiente forma:

@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
}

La nueva clase "WebMvcConfigurerAdapter" queda de la siguiente forma:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.javatutoriales.springmvc.peticiones.controllers")
public class SpringWebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/pages/");
        viewResolver.setSuffix(".jsp");

        return viewResolver;
    }
}

Para cuando trabajamos con archivos en XML existe una etiqueta equivalente, que es:

<mvc:default-servlet-handler/>

Esta simplemente la colocamos en el archivo "springMVCconfig.xml", el cual al final queda de la siguiente forma:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans     
        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <context:component-scan base-package="com.javatutoriales.springmvc.peticiones.controllers" />
    <mvc:annotation-driven  />
    <mvc:default-servlet-handler/>
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
</beans>

Ahora sí, si volvemos a ejecutar la aplicación veremos la siguiente pantalla, correspondiente a nuestra página "index.html":



Si ahora presionamos cualquiera de los botones de la página veremos que en la parte de abajo aparece un mensaje indicando cuál método de nuestro controlador fue llamado.









Esto indica que efectivamente el controlador está eligiendo el método que manejará cada petición en base al método HTTP con el que se hace la petición ^_^.

A continuación, veremos cómo determinar qué método manejará la petición en base al tipo de contenido que enviemos al servidor, o que estemos esperando recibir del mismo. Podemos trabajar con cualquier tipo de contenido, sin embargo nosotros nos limitaremos a los tipos de contenido JSON y XML para obtener la idea de cómo funciona.

"@RequestMapping" tiene, además de los atributos "value" y "method" los atributos "consumes" y "produces", para indicar qué tipo de datos puede recibir y generar, respectivamente. Estos atributos pueden recibir cualquier valor, sin embargo lo más común en recibir información en JSON, XML.
Vamos a agregar un nuevo método llamado "peticionJson". Este método regresará un "ModelAndView" de forma similar a como lo hemos venido trabajando:

public ModelAndView peticionJson() {
    return new ModelAndView("contenido", "metodo", "peticionJson");
}

Anotaremos este método con "@RequestMapping", el cual tendrá como value "/metodo", al igual que los manejadores pasados, y agregaremos el atributo "consumes" para indicar que este método manejará peticiones que estén en formato JSON, esto podemos indicarlo de varias formas. La primera forma es colocar directamente la cadena con el valor "application/json", de esta manera:

@RequestMapping(value = "/metodo", consumes = "application/json")

Sin embargo esto es propenso a errores de escritura o typos difíciles de encontrar, ya que podríamos escribir "aplication/json" o "applicatlon/json" y no darnos cuenta a simple vista. Para evita estos problemas lo recomendable es usar alguno de las constantes de la clase "MediaType", de la siguiente forma:

@RequestMapping(value = "/metodo", consumes = MediaType.APPLICATION_JSON_VALUE)

El método "peticionJson" queda de la siguiente forma:

@RequestMapping(value = "/metodo", consumes = MediaType.APPLICATION_JSON_VALUE)
public ModelAndView peticionJson() {
    return new ModelAndView("contenido", "metodo", "peticionJson");
}

También crearemos un método equivalente a este, pero para recibir peticiones en XML, el método se llamará "peticionXml" y queda de la siguiente forma:

@RequestMapping(value = "/metodo", consumes = MediaType.APPLICATION_XML_VALUE)
public ModelAndView peticionXml() {
    return new ModelAndView("contenido", "metodo", "peticionXml");
}

Ninguno de los dos métodos recibe parámetros, ya que estos deben estar en el formato indicado. Veremos cómo hacer esto en el siguiente tutorial.

Regresamos a "index.html" y agregamos dos botones nuevos después de los 4 que ya tenemos, uno será para realizar la petición en JSON y el otro para la petición en XML:

<button id="botonPeticionJson">Petición Json</button><br />
<button id="botonPeticionXml">Petición Xml</button><br />

Vamos a modificar un poco nuestra función "llamadaAjax" en JavaScript, agreguemos un nuevo parámetro, que será el tipo de información que estamos enviando al servidor:

function llamadaAjax(metodo, contentType)

Para indicar el tipo de datos que estamos enviando, en el caso de la función "ajax" de jQuery, usamos el atributo "contentType". Si recibimos el parámetro anterior queremos usar el valor que recibamos, y en caso contrario usamos el valor por default, que según la documentación de la función "ajax" es "application/x-www-form-urlencoded; charset=UTF-8":

contentType: contentType || 'application/x-www-form-urlencoded; charset=UTF-8'

La función "llamadaAjax" queda de la siguiente forma:

function llamadaAjax(metodo, contentType) {

    $.ajax({
        url: 'tutorial/metodo',
        type: metodo,
        dataType: "text",
        contentType: contentType || 'application/x-www-form-urlencoded; charset=UTF-8',
        success: function (data)
        {
            $('#contenido').html(data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
        }
    });
}

No es necesario modificar las llamadas que ya tenemos a estas funciones, pero sí agregaremos un par de manejadores de clics más además de los que ya tenemos, uno para cada uno de los nuevos botones.

$('#botonPeticionJson').click(function (e) {
});

$('#botonPeticionXml').click(function (e) {
});

Adentro de cada una de estas funciones agregaremos una llamada a "llamadaAjax". En el primer argumento indicaremos que el método HTTP que se usará para la petición será "POST" (aunque podemos usar cualquier otro, ya que en el manejador no indicamos ningún método), en el segundo indicaremos el tipo de contenido "application/json" y "application/xml" según sea el caso. Como estaremos usando estos valores de manera contante, como buena práctica los declararemos como variables:

var JSON_CONTENT_TYPE = "application/json"; 
var XML_CONTENT_TYPE = "application/xml";

$('#botonPeticionJson').click(function (e)
{
    llamadaAjax("POST", JSON_CONTENT_TYPE);
});

$('#botonPeticionXml').click(function (e)
{
    llamadaAjax("POST", XML_CONTENT_TYPE);
});

Ejecutamos nuevamente nuestra aplicación. Cuando presionemos el botón "Petición Json" debemos ver la siguiente pantalla:



Y cuando presionemos el botón "Petición XML" debemos ver esta pantalla:



La funcionalidad del resto de los botones debe permanecer sin cambios.

Podemos hacer algo similar para el caso del tipo de dato que esperamos como respuesta de la petición, en esta ocasión haciendo uso del atributo "produces" de "@RequestMapping". Regresemos a "PeticionesController" y agreguemos dos métodos nuevos similares a los anteriores, pero en este caso se llamarán "respuestaJson" y "respuestaXml".

public ModelAndView respuestaJson() {

}

public ModelAndView respuestaXml() {

}

Ambos responderán también a las peticiones hechas a la URI "/metodo", e indicaremos qué el content type de la respuesta será "application/json" y "application/xml" respectivamente (nuevamente, noten que esta vez en lugar de usar "consumes" usaremos "produces"):

@RequestMapping(value = "/metodo", produces = MediaType.APPLICATION_JSON_VALUE)
    public ModelAndView respuestaJson() {
}

@RequestMapping(value = "/metodo", produces = MediaType.APPLICATION_XML_VALUE)
    public ModelAndView respuestaXml() {
}

Para terminar esta parte, regresamos los objetos "ModelAndView" con los respectivos nombres de los métodos:

@RequestMapping(value = "/metodo", produces = MediaType.APPLICATION_JSON_VALUE)
public ModelAndView respuestaJson() {
    return new ModelAndView("contenido", "metodo", "respuestaJson");
}

@RequestMapping(value = "/metodo", produces = MediaType.APPLICATION_XML_VALUE)
public ModelAndView respuestaXml() {
    return new ModelAndView("contenido", "metodo", "respuestaXml");
}

Regresamos a la página "index.html" y agregamos dos botones nuevos debajo de los que ya tenemos, uno para la petición que regresará la respuesta en JSON y otro para la petición que regresará la respuesta en XML:

<button id="botonRespuestaJson">Respuesta Json</button><br />
<button id="botonRespuestaXml">Respuesta XML</button><br />

Modificaremos nuevamente la función "llamadaAjax" para agregar un tercer parámetro, el cual indicará el tipo de retorno que esperamos tener desde el controlador, el nombre de este parámetro será "accept":

function llamadaAjax(metodo, contentType, accept)

Para indicar el tipo esperado de retorno existe una cabera especial llamada "Accept" que en jQuery colocamos con el valor que pasamos al atributo "headers". Si no se pasa el parámetro "accept" querremos colocar un valor por default, que en este caso es "text/html", el valor lo colocamos de la siguiente forma:

headers: {Accept: accept || "text/html"}

El método "llamadaAjax" completo queda de la siguiente forma:

function llamadaAjax(metodo, contentType, accept) {

    $.ajax({
        url: 'tutorial/metodo',
        type: metodo,
        dataType: "text",
        contentType: contentType || 'application/x-www-form-urlencoded; charset=UTF-8',
        headers: {Accept: accept || "text/html"},
        success: function (data)
        {
            $('#contenido').html(data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
        }
    });
}

Agregaremos dos nuevos manejadores de clic para los botones que acabamos de poner. Ambos pasarán null como el segundo argumento, pero para el tercero colocaremos la variable correspondiente al tipo JSON o XML, de la siguiente forma:

$('#botonRespuestaJson').click(function (e)
{
    llamadaAjax("POST", null, JSON_CONTENT_TYPE);
});

$('#botonRespuestaXml').click(function (e)
{
    llamadaAjax("POST", null, XML_CONTENT_TYPE);
});

Ejecutamos nuevamente nuestra aplicación, con lo que deberemos ver la siguiente pantalla, con nuestros dos nuevos botones:



Al hacer clic en cada botón veremos el mensaje correspondiente, para el caso de la respuesta en JSON:



Y para la respuesta en XML:



El resto de los botones debe seguir funcionando de manera normal.

Spring MVC realiza por default algo conocido como "Suffix Pattern Matching", eso quiere decir que un controlador por default está mapeado a la ruta que le indiquemos ("/metodo" en el ejemplo anterior), pero también a "/metodo.*". Esto hace que sea fácil solicitar distintas representaciones de un recurso a través de una URL, como por ejemplo "metodo.json" o "metodo.xml". Cualquiera de las peticiones anteriores hará que la petición sea manejada por el método que regresa el tipo de dato correspondiente.

Hagamos una pequeña modificación en los manejadores del evento "clic" para nuestros dos últimos botones, de forma que podamos comprobar esto. En este caso en el botón "botonRespuestaJson" haremos una petición a la URL "tutorial/metodo.json", de la siguiente forma:

$.ajax({
    url: 'tutorial/metodo.json',
    type: 'POST',
    dataType: "text",
    success: function (data)
    {
        $('#contenido').html(data);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
    }
});

Noten que en este caso no estamos indicando ni tipo de datos para la petición ni para la respuesta.

Para el botón "botonRespuestaXml" haremos algo parecido, pero en este caso la petición será a la URL "tutorial/metodo.xml", de la siguiente forma:

$.ajax({
    url: 'tutorial/metodo.xml',
    type: 'POST',
    dataType: "text",
    success: function (data)
    {
        $('#contenido').html(data);
    },
    error: function (xhr, ajaxOptions, thrownError) {
        $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
    }
});

Si ejecutamos nuevamente la aplicación, veremos que ambos botones continúan llamando a sus manejadores correspondientes.

Usando ambos métodos podemos incluso jugar con combinaciones de tipos de datos en la petición y en la respuesta, pudiendo enviar un tipo de dato y solicitar otro tipo para la respuesta. De esta forma podríamos tener un manejador que reciba una petición en JSON y regrese su respuesta en JSON, y otro manejador de reciba una petición en JSON y regrese su respuesta en XML; de forma similar para el XML o cualquier otro tipo de dato.

Hagamos nuevamente una modificación para comprobar esto. Primero en la clase "PeticionesController" agregaremos cuatro nuevos manejadores de peticiones, todos ellos para la URI "/metodo", que es la misma que hemos estado manejando hasta el momento. Dos de estos manejadores recibirán peticiones en JSON, uno regresará JSON y el otro XML. Los otros dos manejadores recibirán peticiones en XML y regresarán JSON y XML respectivamente. A estos métodos les pondremos nombres acordes a lo que reciben y envían:

@RequestMapping(value = "/metodo", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ModelAndView peticionJsonRespuestaJson() {
    return new ModelAndView("contenido", "metodo", "peticionJsonRespuestaJson");
}

@RequestMapping(value = "/metodo", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public ModelAndView peticionJsonRespuestaXml() {
    return new ModelAndView("contenido", "metodo", "peticionJsonRespuestaXml");
}

@RequestMapping(value = "/metodo", method = RequestMethod.POST, consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ModelAndView peticionXmlRespuestaJson() {
    return new ModelAndView("contenido", "metodo", "peticionXmlRespuestaJson");
}

@RequestMapping(value = "/metodo", method = RequestMethod.POST, consumes = MediaType.APPLICATION_XML_VALUE, produces = MediaType.APPLICATION_XML_VALUE)
public ModelAndView peticionXmlRespuestaXml() {
    return new ModelAndView("contenido", "metodo", "peticionXmlRespuestaXml");
}

Regresamos a "index.html" y agregamos cuatro nuevos botones, uno por cada combinación de petición-respuesta que usaremos:

<button id="botonPeticionJsonRespuestaJson">Petición Json Respuesta Json</button><br />
<button id="botonPeticionJsonRespuestaXml">Petición Json Respuesta Xml</button><br />
<button id="botonPeticionXmlRespuestaJson">Petición Xml Respuesta Json</button><br />
<button id="botonPeticionXmlRespuestaXml">Petición Xml Respuesta Xml</button><br />

Finalmente, agregaremos un manejador de clic para cada uno de los botones, usando el método "llamadaAjax" que ya tenemos, de la siguiente forma:

$('#botonPeticionJsonRespuestaJson').click(function (e)
{
    llamadaAjax("POST", JSON_CONTENT_TYPE, JSON_CONTENT_TYPE);
});

$('#botonPeticionJsonRespuestaXml').click(function (e)
{
    llamadaAjax("POST", JSON_CONTENT_TYPE, XML_CONTENT_TYPE);
});

$('#botonPeticionXmlRespuestaJson').click(function (e)
{
    llamadaAjax("POST", XML_CONTENT_TYPE, JSON_CONTENT_TYPE);
});

$('#botonPeticionXmlRespuestaXml').click(function (e)
{
    llamadaAjax("POST", XML_CONTENT_TYPE, XML_CONTENT_TYPE);
});

Al ejecutar la aplicación debemos ver la siguiente pantalla:



Y al hacer clic en los botones correspondientes veremos que se invoca el manejador de peticiones adecuado:









El resto de los botones continúa teniendo su funcionalidad normal.

Spring MVC va aún más allá para poder indicar los manejadores de peticiones, llegando incluso a detalles como los parámetros o cabeceras que se están recibiendo. Para el primero de los casos usamos el atributo "params" de la anotación "@RequestMapping", en este atributo indicamos qué parámetros debe tener la petición para ser manejada por este método.

Agregaremos tres métodos nuevos, el primero manejará las peticiones que tengan un parámetro llamado "uno", el segundo las que tengan un parámetro llamado "dos", y el tercero las que tengan ambos parámetros. Los tres métodos quedan de la siguiente forma:

@RequestMapping(params = "uno", value = "/metodo")
public ModelAndView parametroUno() {
    return new ModelAndView("contenido", "metodo", "parametroUno");
}

@RequestMapping(params = "dos", value = "/metodo")
public ModelAndView parametroDos() {
    return new ModelAndView("contenido", "metodo", "parametroDos");
}

@RequestMapping(params = { "uno", "dos" }, value = "/metodo")
public ModelAndView parametroUnoDos() {
    return new ModelAndView("contenido", "metodo", "parametroUnoDos");
}

Para poder pasar estos parámetros desde la página "index.html", haremos uso de tres formularios en los que pasaremos los parámetros como campos de tipo "hidden", aunque en realidad esto no es necesario, podemos pasarlos como cualquier campo que necesitemos (texto, checkbox, select, etc.).

<form action="tutorial/metodo" method="POST">
    <input type="hidden" name="uno" value="uno" />
    <input value="Enviar Uno" type="submit" />
</form>

<form action="tutorial/metodo" method="POST">
    <input type="hidden" name="dos" value="dos" />
    <input value="Enviar Dos" type="submit" />
</form>

<form action="tutorial/metodo" method="POST">
    <input type="hidden" name="uno" value="uno" />
    <input type="hidden" name="dos" value="dos" />
    <input value="Enviar Uno Dos" type="submit" />
</form>

Ejecutamos nuevamente la aplicación, con lo que debemos ver la siguiente página:



Al hacer clic en cada uno de los botones, veremos los mensajes correspondientes a los manejadores:








El resto de los botones continúa funcionando de manera normal.

Para terminar este tutorial, veremos cómo crear un manejador que responda dependiendo de las cabeceras que tenga la petición. Primero agregaremos el manejador, el cual será muy parecido a los anteriores, sólo que en esta ocasión usaremos el atributo "headers" para indicar cuál encabezado es que el que esperamos que tenga el método, en este caso el nombre de la cabecera será "JavaTutoriales". El manejador queda de la siguiente forma:

@RequestMapping(headers = { "JavaTutoriales" }, value = "/metodo")
public ModelAndView cabeceraJavaTutoriales() {
    return new ModelAndView("contenido", "metodo", "cabeceraJavaTutoriales");
}
Ahora, de regreso a "index.html" agregamos un botón para hacer la petición:

<button id="botonCabeceraJavaTutoriales">Petición Cabecera Java Tutoriales</button><br />

Y un manejador del evento "click" que se encargue de enviar la petición al método correspondiente, agregando la cabecera "JavaTutoriales", para nuestro caso el valor de esta no es importante, así que nosotros le pondremos el valor "Spring MVC", de la siguiente forma:

$('#botonCabeceraJavaTutoriales').click(function (e)
{
    $.ajax({
        url: 'tutorial/metodo',
        type: 'POST',
        dataType: "text",
        headers: {JavaTutoriales:"SpringMVC"},
        success: function (data)
        {
            $('#contenido').html(data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            $('#contenido').html('Error ' + xhr.status + ": " + thrownError + ".");
        }
    });
});

Ahora que todo está colocado en su lugar, ejecutamos nuevamente la aplicación con lo que debemos ver la siguiente pantalla:



Al hacer clic en el botón "Petición Xml Respuesta Xml" debemos ver la siguiente salida:



Con lo que podemos comprobar que el manejador fue llamado correctamente.

Como pudimos ver en este tutorial, hay muchas maneras de indicar qué método manejará las peticiones que haga nuestra aplicación, y tenemos en realidad una gran gama de posibilidades; en este tutorial tenemos 16 métodos que responden a distintas peticiones hechas a la misma URL, y cada una se manda llamar de manera correcta, ya sea por el tipo de parámetros que se reciben o devuelven, por los parámetros o cabeceras que se reciben, etc. Como podemos ver, usando esta combinación de datos podemos indicar exactamente cuáles serán las condiciones con las que se puede llamar a un método particular.

Espero que este tutorial les sea de utilidad. Si tienen alguna duda, sugerencia, comentario o aclaración, pueden dejarla en la sección de comentarios o enviar un correo a programadorjavablog@gmail.com (pueden agregarme al chat de gmail). También pueden seguir JavaTutoriales en las siguientes redes sociales:
Descarga los archivos de este tutorial desde aquí:

Entradas Relacionadas:

8 comentarios:

  1. Excelente, gracias por compartir tu conocimiento. Saludes.

    ResponderEliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  3. Muchas gracias! Excelentes tutoriales y por supuesto esperando el tercero.

    ResponderEliminar
  4. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  5. Excelente tutorial!! Esperamos los siguientes!

    ResponderEliminar