El método
Ante cualquier operación que tengas que exponer, esta secuencia. Es la de la UD1 con dos pasos nuevos, los dos últimos:
- ¿Sobre qué recurso actúo, y es uno o una colección?
- ¿Qué le hago? Eso elige el método HTTP, y con él si es idempotente
- ¿Qué datos necesito? Ruta si identifican, query si filtran, cuerpo si son contenido
- ¿Cómo puede terminar esto? Cada final tiene su código de estado
- ¿Cómo demuestro que sigue funcionando mañana? Una petición guardada con su comprobación
El paso cuatro es la unidad entera resumida en una línea: una operación no tiene un resultado, tiene varios finales posibles, y cada uno se comunica con un código distinto. Pensar solo en el caso que sale bien es lo que produce APIs que responden 200 a todo.
La idea más importante
El código de estado no constituye un detalle de la respuesta, sino la parte sobre la que el cliente decide. Un
200es una afirmación, y afirmar que todo ha ido bien cuando no has encontrado nada es mentir con buena sintaxis.
De ahí sale el resto de la unidad. Por eso un recurso ausente es 404 y una lista vacía no lo es, por eso crear devuelve 201 y dice dónde, por eso borrar devuelve 204, y por eso hoy sabes que un cuerpo incompleto que devuelve 200 es un problema aunque no lance ninguna excepción.
Un contrato es lo que prometes, no lo que te sale
Tu API promete unas rutas, unos formatos y unos códigos. Mientras eso se cumpla, quien te consume puede confiar. La colección de la sesión 7 existe justamente para demostrar cada día que la promesa sigue en pie.
Las decisiones que tienes que saber justificar
| Decisión | Lo que tienes que poder decir |
|---|---|
Una colección vacía es 200 con [] |
La ruta existe y la consulta se resolvió; no hay elementos, que no es lo mismo que no haber recurso |
Crear devuelve 201 con Location |
El cliente necesita saber que hay algo nuevo y dónde encontrarlo, sin construir la URL él |
Borrar devuelve 204 |
Ha ido bien y no hay nada que entregar; un 200 vacío promete contenido que no llega |
Modificar es PUT o PATCH, no POST |
POST no es idempotente, y un reintento tras un fallo de red duplicaría el recurso |
PUT pierde lo que no envías |
Declara el recurso completo; si quieres tocar un campo, la operación es PATCH |
| El id manda desde la ruta | Si la ruta y el cuerpo discrepan hay que elegir uno, y adivinar es peor que decidir |
| Jackson tolerante o estricto | Tolerante protege a clientes antiguos; estricto detecta erratas. Las dos son defendibles, no decidirlo no |
| La colección se entrega con el código | Una prueba que solo existe en tu portátil no demuestra nada a nadie |
Al terminar deberías poder responder
- Enumera las fases por las que pasa una petición desde Tomcat hasta tu método.
- ¿Qué es el
DispatcherServlety por qué no lo escribes tú? - No aparece la línea
Mapped toen el registro. ¿Qué ha ocurrido? - ¿Qué fase produce un
404, cuál un405, cuál un415y cuál un406? - ¿Por qué en esos cuatro casos tu método no llega a ejecutarse?
- ¿De dónde puede salir cada parámetro de un método de controlador?
- ¿Recorre Jackson las claves del JSON o los campos de tu clase, y qué consecuencia tiene?
- Diferencia entre un cuerpo inválido y uno incompleto. ¿Quién resuelve cada uno?
- ¿Por qué
{}devuelve200y{,}devuelve400? - ¿Qué ganas y qué pierdes al activar
fail-on-unknown-properties? - ¿En qué formato viaja una fecha en una API y por qué no en el del país?
- ¿Qué significa que una operación sea segura? ¿Y idempotente?
- ¿Por qué le importa la idempotencia a un cliente que ha sufrido un tiempo de espera agotado?
- ¿Qué le pasa a un campo que no envías en un
PUT? ¿Y en unPATCH? - ¿Por qué tu
PATCHactual no puede vaciar un campo? - ¿Cuándo basta
@ResponseStatusy cuándo hace faltaResponseEntity? - ¿Qué información lleva la cabecera
Locationy en qué respuesta viaja? - ¿Por qué una lista vacía no es un
404? - ¿Qué es una regresión y por qué es cara?
- ¿Por qué una comprobación que exige que el id sea exactamente 3 es una mala comprobación?
Si además puedes recibir una especificación de endpoints y traducirla a controladores con sus códigos correctos y su colección, estás listo para la UD3.
El vocabulario de la unidad
| Concepto | Significa |
|---|---|
| DispatcherServlet | La puerta única por la que entran todas las peticiones |
| Handler mapping | La fase que decide qué método tuyo atiende una petición |
| Resolutor de argumentos | La pieza que construye cada parámetro de tu método |
| Conversor de mensaje | Quien traduce entre el cuerpo HTTP y los objetos Java |
@RequestHeader |
Lee una cabecera de la petición |
consumes |
Qué formatos sabe leer un endpoint. Su incumplimiento da 415 |
produces |
Qué formatos sabe devolver. Su incumplimiento da 406 |
| Deserializar | Convertir el texto del cuerpo en un objeto Java |
| Cuerpo inválido | No es JSON o no encaja con los tipos. Lo rechaza el framework con 400 |
| Cuerpo incompleto | Es JSON válido y le faltan datos. Nadie lo rechaza todavía |
| Tolerancia a claves desconocidas | Ignorar en silencio lo que no reconoce, activado por defecto |
| Seguro | No modifica nada. Solo GET |
| Idempotente | Repetirlo deja el servidor igual que hacerlo una vez |
PUT |
Sustituye el recurso completo por lo que envías |
PATCH |
Modifica solo los campos enviados |
ResponseEntity |
La respuesta entera: estado, cabeceras y cuerpo, decididos por ti |
@ResponseStatus |
Fija el código cuando el método solo puede terminar de una manera |
201 Created |
Se ha creado un recurso; va acompañado de Location |
204 No Content |
Ha ido bien y no hay nada que devolver |
Location |
La URL donde vive el recurso recién creado |
| Colección | Peticiones guardadas, nombradas y ordenadas como un escenario ejecutable |
| Variable | Un hueco con nombre dentro de una petición: {{baseUrl}} |
| Entorno | El juego de valores que rellena esas variables |
| Encadenar | Guardar un dato de una respuesta para usarlo en la petición siguiente |
| Regresión | Algo que funcionaba y se ha roto por un cambio hecho en otro sitio |
| Contrato | Las rutas, formatos y códigos que tu API promete cumplir |
Comprobación final del producto
Comprobación final · con el proyecto delante
- Ningún endpoint responde
200cuando el recurso no existe. - Crear responde
201conLocation, y esa URL funciona al pegarla en unGET. - Borrar responde
204y su método devuelveResponseEntity<Void>. PUTyPATCHse comportan de forma distinta y sabes demostrarlo con dos peticiones.- La colección cubre la especificación entera y se ejecuta dos veces seguidas en verde.
- El repositorio incluye la colección exportada, el README y las decisiones escritas.
- Sabes provocar a voluntad un 400, un 404, un 405, un 406 y un 415.
Resultados de la unidad
- Seguir una petición desde el cliente HTTP hasta el método del controller.
- Recibir JSON y transformarlo en objetos Java de forma controlada.
- Implementar las operaciones de escritura con el método HTTP que les corresponde.
- Controlar cuerpo, cabeceras y código de estado mediante ResponseEntity.
- Convertir pruebas manuales sueltas en una colección con variables y entornos.
La siguiente unidad
En la UD1 preguntábamos cómo conseguir que la aplicación respondiera. En esta, cómo conseguir que respondiera bien. Queda la tercera pregunta, y es la que separa una API que funciona de una que se puede usar:
¿Está bien diseñada?
- UD1 · que responda
- UD2 · que responda correctamente
- UD3 · que esté bien diseñada
Tienes los seis motivos delante, todos comprobados por ti en la sesión 8:
| Lo que tu API sigue haciendo mal | Se arregla en |
|---|---|
| Acepta una tarea sin título, sin prioridad y sin nada | UD3, con validación |
| Acepta una tarea de un proyecto que no existe | UD3 |
| Sus errores no explican qué hay que corregir | UD3, con errores coherentes |
| Publica el modelo interno íntegro, sin decidir qué campos salen | UD3, con DTO |
| Sus rutas las has ido nombrando por intuición | UD3, con diseño orientado a recursos |
| Al reiniciar se pierde todo | UD5, con PostgreSQL |
El trabajo de estas dos semanas demuestra aquí su utilidad: cuando en la UD3 aparezcan los DTO, las anotaciones de validación y el manejador de errores, no serán temas nuevos. Serán las respuestas a seis problemas que ya has visto fallar, con una colección lista para demostrar que se han arreglado.
Ya deberías ser capaz de
- Seguir una petición desde el cliente HTTP hasta el método del controller.
- Recibir JSON y transformarlo en objetos Java de forma controlada.
- Implementar las operaciones de escritura con el método HTTP que les corresponde.
- Controlar cuerpo, cabeceras y código de estado mediante ResponseEntity.
- Convertir pruebas manuales sueltas en una colección con variables y entornos.