← Peticiones, respuestas y CRUD en memoria

UD2 · Comunicar

Lo que debes recordar

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:

Cómo se decide una operación completa
  1. ¿Sobre qué recurso actúo, y es uno o una colección?
  2. ¿Qué le hago? Eso elige el método HTTP, y con él si es idempotente
  3. ¿Qué datos necesito? Ruta si identifican, query si filtran, cuerpo si son contenido
  4. ¿Cómo puede terminar esto? Cada final tiene su código de estado
  5. ¿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 200 es 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

  1. Enumera las fases por las que pasa una petición desde Tomcat hasta tu método.
  2. ¿Qué es el DispatcherServlet y por qué no lo escribes tú?
  3. No aparece la línea Mapped to en el registro. ¿Qué ha ocurrido?
  4. ¿Qué fase produce un 404, cuál un 405, cuál un 415 y cuál un 406?
  5. ¿Por qué en esos cuatro casos tu método no llega a ejecutarse?
  6. ¿De dónde puede salir cada parámetro de un método de controlador?
  7. ¿Recorre Jackson las claves del JSON o los campos de tu clase, y qué consecuencia tiene?
  8. Diferencia entre un cuerpo inválido y uno incompleto. ¿Quién resuelve cada uno?
  9. ¿Por qué {} devuelve 200 y {,} devuelve 400?
  10. ¿Qué ganas y qué pierdes al activar fail-on-unknown-properties?
  11. ¿En qué formato viaja una fecha en una API y por qué no en el del país?
  12. ¿Qué significa que una operación sea segura? ¿Y idempotente?
  13. ¿Por qué le importa la idempotencia a un cliente que ha sufrido un tiempo de espera agotado?
  14. ¿Qué le pasa a un campo que no envías en un PUT? ¿Y en un PATCH?
  15. ¿Por qué tu PATCH actual no puede vaciar un campo?
  16. ¿Cuándo basta @ResponseStatus y cuándo hace falta ResponseEntity?
  17. ¿Qué información lleva la cabecera Location y en qué respuesta viaja?
  18. ¿Por qué una lista vacía no es un 404?
  19. ¿Qué es una regresión y por qué es cara?
  20. ¿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 200 cuando el recurso no existe.
  • Crear responde 201 con Location, y esa URL funciona al pegarla en un GET.
  • Borrar responde 204 y su método devuelve ResponseEntity<Void>.
  • PUT y PATCH se 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?

Las tres preguntas de un backend
  1. UD1 · que responda
  2. UD2 · que responda correctamente
  3. 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.