Una API es la puerta de entrada a nuestra aplicación: recibe peticiones desde el exterior, extrae los datos que le envían, ejecuta la lógica necesaria y devuelve una respuesta. Hasta ahora, en nuestra API HelloWorld solo recibimos peticiones y damos una respuesta. El siguiente paso es permitir que un cliente nos pase información a través de la URL.

Formas en las que una API puede recibir datos

El componente encargado de extraer los datos de la petición es el controlador. En una API HTTP, éstos pueden llegar al endpoint de varias maneras según su finalidad:

  1. En la URL como parámetros
  2. En la URL como parte de la ruta
  3. En el cuerpo de la petición
  4. En las cabeceras de la petición
  5. En cookies
  6. Como formularios
  7. Como ficheros

Vamos a ver la primera y más sencilla de ellas: recibir datos como parámetros de la URL.

Primer dato de entrada en nuestra API

Nuestra API de momento, está devolviendo un saludo genérico. A partir de aquí, empezamos a construir APIs que reaccionan a lo que les envía el cliente.

http://localhost:8080/hello?name=Juan

Para recibirlo en el controlador y poder utilizarlo, incorporamos un parámetro con el mismo nombre al método del endpoint y lo anotamos con @RequestParam. De esta forma Spring Boot extrae automáticamente el valor de la URL y lo pasa como argumento al método.

1
2
3
4
@GetMapping("/hello")  
public String hello(@RequestParam String name) {  
    return "Hello " + name + " from Spring Boot!";
}

Si desplegamos la aplicación y probamos desde el navegador, vemos el resultado:

Navegador web con la respuesta de la API al pasar parámetro en la URL

Pero, ¿y si no incluimos el parámetro name en la URL?

Parámetros opcionales

Con la configuración actual, la aplicación está esperando siempre recibir el parámetro name en la petición, devolviendo un error de no ser así.

Para evitarlo, podemos marcar los parámetros como opcionales con la propiedad required de la siguiente manera:

1
2
3
4
5
6
7
@GetMapping("/hello")  
public String hello(@RequestParam(required = false) String name) {  
    if( name == null ) {  
        return "Hello World from Spring Boot!";  
    }  
    return "Hello " + name + " from Spring Boot!";  
}

En este caso, si no se envía el parámetro, Spring asigna el valor null.

Navegador web con la respuesta de la API por defecto al no recibir parámetro en la URL

Valores por defecto

La anotación cuenta con la propiedad defaultValue que nos permite simplificar la lógica en el controlador, fijando un valor por defecto si no se informa el parámetro, en cuyo caso Spring asume automáticamente que se trata de un parámetro opcional. Cuando usamos defaultValue, Spring considera automáticamente que el parámetro es opcional, por lo que no es necesario indicar required = false.

1
2
3
4
@GetMapping("/hello")  
public String hello(@RequestParam(defaultValue = "World") String name) {  
    return "Hello " + name + " from Spring Boot!";  
}

Cuando usar parámetros en la URL

Los @RequestParam se utilizan cuando queremos recibir datos simples en la URL, normalmente para:

  • filtros
  • búsquedas
  • valores opcionales

Los tests como red de seguridad ante cambios

Cada vez que cambiamos el comportamiento de un endpoint, los tests nos ayuda a comprobar si de cara al cliente el cambio ha sido transparente. No solo validan el código, también documentan cómo debe comportarse la API.

Si lanzamos los tests que ya habíamos implementado, vemos que siguen ejecutándose correctamente. Esto ocurre porque hemos mantenido el comportamiento por defecto gracias al uso de defaultValue.

1
2
3
4
5
6
@Test  
void helloEndpointReturnsHelloWorld() throws Exception {  
    mockMvc.perform( get("/hello") )  
            .andExpect( status().isOk() )  
            .andExpect( content().string("Hello World from Spring Boot!") );  
}

Consola donde se comprueba que los tests han finalizado correctamente

Los tests evolucionan con el código

Unos test útiles y completos deben cubrir los distintos escenarios posibles, y deben ampliarse junto con el código de la API. Nuestro test cubre el comportamiento por defecto, pero deberemos completarlo ahora para el caso de recibir un parámetro.

1
2
3
4
5
6
@Test  
void helloWithParamReturnsCustomName() throws Exception {  
    mockMvc.perform(get("/hello").param("name", "Juan"))  
            .andExpect(status().isOk())  
            .andExpect(content().string("Hello Juan from Spring Boot!"));  
}

Elementos nuevos que usa el test

Herramientas para simular peticiones HTTP

  • param(): Spring Boot añade este parámetro a la URL como si el cliente lo hubiera enviado en la petición real.

Nombres que reflejen intención

Adicionalmente, para reflejar mejor qué caso valida cada test, actualizamos también el nombre del test previo:

1
2
3
4
5
6
@Test  
void helloWithoutParamReturnsDefaultValue() throws Exception {  
    mockMvc.perform( get("/hello") )  
            .andExpect( status().isOk() )  
            .andExpect( content().string("Hello World from Spring Boot!") );  
}

Lanzamos de nuevo la ejecución de los tests y comprobamos que finalizan correctamente.

Conclusiones

Las APIs son las puertas de entrada a nuestras aplicaciones por lo que será habitual recibir datos en alguno de los formatos y por alguna de las vías que proporciona Spring Boot. Los parámetros en la URL son una de las más sencillas y útiles. A partir de aquí, iremos incorporando nuevas formas de recibir datos en nuestra API a medida que evolucionamos sus funcionalidades.

También, cada vez que cambiamos el comportamiento de nuestra API, los tests nos ayudan a validar y revisar los cambios de forma que podamos detectar si estamos modificando algún caso de uso que no teníamos contemplado, pero para que sigan siendo útiles, deben completarse a medida que ampliamos nuestra API.