← El cliente del proyecto: navegador y CORS

UD8 · Conectar

Lo que debes recordar

El método

En esta unidad has dado un paso fundamental en tu formación como desarrollador backend: conectar por primera vez tu servidor con un cliente real en el navegador web sin intermediarios ni frameworks opacos.

Para integrar cualquier cliente con tu API REST de forma robusta, aplica siempre este protocolo de conexión:

El protocolo de integración cliente-servidor
  1. Sirve el cliente web siempre desde un servidor HTTP local, jamás mediante el protocolo local file:///.
  2. Consume los endpoints con la API nativa fetch() en dos fases asíncronas verificando siempre response.ok.
  3. Comprende que el navegador aplica la Política del Mismo Origen (SOP) para proteger la seguridad del usuario.
  4. Configura CORS en Spring Boot de forma centralizada con WebMvcConfigurer acotando los orígenes autorizados.
  5. Permite el intercambio de métodos y cabeceras necesarios, exponiendo la cabecera Location para respuestas de creación.
  6. Evita el recargo de página en formularios interceptando el evento con event.preventDefault().
  7. Serializa los cuerpos de petición con JSON.stringify() declarando siempre Content-Type: application/json.
  8. Aprovecha el estándar RFC 7807 (Problem Details) para pintar errores de validación campo a campo en la interfaz.
  9. Aplica el diagnóstico de tres capas (Consola JS, Red DevTools y Logs de Spring Boot) antes de modificar código ante un fallo.

La idea más importante

Postman es un tester en un entorno estéril; el navegador es un entorno hostil gobernado por la Política del Mismo Origen. La verdadera prueba de fuego de una API REST no es que responda a tus peticiones manuales, sino que permita a una página web interactuar con ella sin brechas de seguridad ni fricciones de integración.

Entender la diferencia entre cómo procesa el tráfico un proceso de escritorio y cómo lo filtra un navegador web separa a los programadores aficionados de los ingenieros de software. Cuando domines CORS y la inspección de red, los fallos de integración dejarán de ser misterios y se convertirán en diagnósticos exactos en menos de un minuto.

Las decisiones que tienes que saber justificar

Decisión de ingeniería Lo que tienes que poder defender ante un tribunal
Cliente nativo sin frameworks en UD8 Aislar la integración web y CORS de la complejidad añadida de Angular, React o sistemas de empaquetado (Vite, Webpack), garantizando que el estudiante comprenda el protocolo HTTP puro.
Servidor web local frente a file:/// El protocolo file:/// carece de origen HTTP válido (origin: null), desactiva cookies y provoca comportamientos anómalos en las políticas de seguridad del navegador.
fetch() con comprobación de response.ok La función fetch() solo rechaza la promesa ante fallos de red físicos; si el servidor responde 400 o 500, la promesa se resuelve y debe ser interceptada mediante !response.ok.
Mismo origen definido por Tríada Dos recursos solo comparten origen si coinciden exactamente Esquema, Host y Puerto; un frontend en el puerto 5500 y un backend en el 8080 son estrictamente orígenes cruzados (Cross-Origin).
Por qué Postman no sufre CORS CORS es una política de seguridad ejecutada por el navegador para proteger al usuario frente a scripts maliciosos; Postman no ejecuta scripts web no confiables ni comparte sesiones de navegación.
Petición Preflight (OPTIONS) El navegador valida previamente si el servidor autoriza el método y las cabeceras complejas (como application/json) antes de arriesgarse a ejecutar la petición real que podría alterar datos.
Orígenes acotados frente a comodín * En entornos reales con autenticación y credenciales (allowCredentials = true), el estándar prohíbe el comodín * para evitar que cualquier sitio web malicioso secuestre sesiones de usuario.
event.preventDefault() en formularios Cancela el envío síncrono predeterminado de formularios HTML, permitiendo que JavaScript capture los datos, los serialice a JSON y gestione la respuesta de forma asíncrona.
Traducción de RFC 7807 a la UI Exponer errores de validación con formato estándar permite al cliente recorrer el objeto invalidParams y resaltar con precisión milimétrica el campo erróneo en el formulario.
Inspección en tres capas Frente a un fallo, examinar en orden Consola JS (cliente), Pestaña Network (transporte) y Terminal Spring Boot (servidor) localiza la causa raíz sin conjeturas ni cambios de código a ciegas.

Al terminar la unidad deberías poder responder

  1. ¿Qué tres componentes definen un origen web según la Política del Mismo Origen (Same-Origin Policy)?
  2. ¿Por qué una petición emitida desde http://localhost:5500 hacia http://localhost:8080 requiere habilitar CORS?
  3. ¿Por qué una petición que falla por CORS en el navegador se ejecuta con éxito en Postman o Bruno?
  4. ¿En qué fase de la petición actúa la llamada previa (Preflight Request) y qué método HTTP utiliza?
  5. ¿Qué cabecera HTTP debe enviar Spring Boot para que el navegador autorice la lectura de la respuesta?
  6. ¿Por qué está prohibido utilizar el comodín Access-Control-Allow-Origin: * si la API utiliza allowCredentials(true)?
  7. ¿Para qué sirve la cabecera Access-Control-Expose-Headers al devolver una respuesta 201 Created?
  8. ¿Cómo se configura CORS de forma centralizada y profesional en Spring Boot utilizando WebMvcConfigurer?
  9. ¿Por qué el código fetch(url) se ejecuta en dos fases con dos llamadas a await consecutivas?
  10. ¿Qué ocurre si un endpoint devuelve un error 500 Internal Server Error y el script de JavaScript no comprueba res.ok?
  11. ¿Por qué nunca se debe abrir una página cliente haciendo doble clic en el archivo HTML (file:///) durante el desarrollo?
  12. ¿Qué información técnica muestra el panel Network de DevTools al seleccionar una petición HTTP?
  13. ¿Qué significa el indicador TTFB (Time to First Byte) en la cascada de tiempos de red del navegador?
  14. ¿Por qué es obligatorio llamar a event.preventDefault() al capturar el evento submit de un formulario?
  15. ¿Qué función de JavaScript se encarga de convertir un objeto en memoria a formato texto JSON para enviarlo en el body de una petición?
  16. ¿Qué cabecera Content-Type debe incluir el cliente para que el backend Spring Boot entienda que el cuerpo es un JSON y no texto plano?
  17. ¿Cómo estructura el estándar RFC 7807 los errores de validación para que el cliente pueda pintarlos bajo cada campo?
  18. ¿Qué código HTTP semántico debe devolver la API tras un borrado exitoso con el método DELETE?
  19. ¿Cuál es el orden estricto de las tres capas que deben inspeccionarse al diagnosticar un fallo entre cliente y servidor?
  20. ¿Qué diferencia hay entre un error net::ERR_CONNECTION_REFUSED y un error de CORS en la consola del navegador?

El vocabulario de la unidad

Concepto Significa
Same-Origin Policy (SOP) Mecanismo de seguridad crítico de los navegadores que restringe cómo un documento o script de un origen puede interactuar con recursos de otro origen.
Origen La combinación unívoca de Esquema (protocolo), Host (dominio) y Puerto (http://localhost:8080).
CORS Cross-Origin Resource Sharing: estándar que permite a los servidores declarar mediante cabeceras HTTP qué orígenes externos tienen permiso para leer sus datos.
Preflight Request Petición previa automática con método OPTIONS enviada por el navegador para comprobar los permisos de CORS antes de ejecutar la petición real.
OPTIONS Método HTTP que consulta las opciones de comunicación y métodos permitidos por un servidor para un recurso determinado.
Access-Control-Allow-Origin Cabecera HTTP emitida por el backend que especifica qué orígenes tienen autorización para acceder al recurso en el navegador.
Access-Control-Expose-Headers Cabecera del servidor que autoriza al código JavaScript del navegador a leer cabeceras de respuesta que no están en la lista blanca por defecto.
Sandbox del navegador Entorno de aislamiento estricto donde el navegador ejecuta scripts web para impedir el acceso indebido al sistema operativo o a datos de otros sitios.
Fetch API Interfaz nativa de JavaScript basada en promesas para realizar peticiones HTTP asíncronas en navegadores modernos.
response.ok Propiedad booleana del objeto Response de fetch que devuelve true si el código de estado HTTP se encuentra en el rango 200-299.
JSON.stringify() Método de JavaScript que transforma un objeto o array en una cadena de texto en formato JSON válida para su transmisión por red.
event.preventDefault() Método que detiene el comportamiento por defecto de un evento del navegador, como la recarga síncrona de página en formularios.
DevTools Network Panel de herramientas del navegador para inspeccionar en tiempo real el tráfico HTTP, cabeceras, cargas útiles y tiempos de latencia.
TTFB (Time to First Byte) Tiempo transcurrido desde que el cliente emite la petición HTTP hasta que recibe el primer byte de respuesta del servidor.
Diagnóstico en 3 capas Protocolo de resolución de incidencias que examina secuencialmente la Consola JS (cliente), la Red DevTools (transporte) y los Logs de Spring (servidor).

Comprobación final del producto de la unidad

Auditoría de integración web · criterios de producción

  • La página web (index.html) se sirve desde un servidor HTTP local y consume la API real sin depender de ningún framework.
  • La configuración de CORS en Spring Boot está centralizada en una clase WebConfig que implementa WebMvcConfigurer.
  • Los orígenes autorizados están explícitamente declarados y externalizados en application.properties sin comodines universales (*).
  • La cabecera Location está expuesta mediante exposedHeaders("Location") para permitir su lectura en el cliente.
  • Las peticiones Preflight (OPTIONS) son respondidas con éxito (código 200 o 204) por el backend.
  • El cliente maneja el ciclo completo de lectura (GET), creación (POST) y borrado (DELETE) mediante fetch() asíncrono.
  • Los formularios capturan el evento con event.preventDefault() y transmiten el cuerpo serializado con JSON.stringify().
  • Los errores de validación de Bean Validation (RFC 7807) se procesan en el cliente y se muestran junto a los campos correspondientes.
  • La consola de JavaScript del navegador no arroja advertencias ni errores en rojo de CORS ni de promesas no capturadas.
  • El estudiante es capaz de diagnosticar en menos de un minuto si un fallo reside en el script cliente, en la red o en el servidor Spring Boot.

Resultados de la unidad

  • Consumir la API desde el navegador con fetch, sin ningún framework.
  • Explicar qué es CORS, por qué lo aplica el navegador y por qué Postman no lo sufre.
  • Configurar CORS en el backend de forma explícita y acotada.
  • Diagnosticar un fallo de integración sabiendo si el problema es del cliente, del servidor o del navegador.

Ya deberías ser capaz de

  • Consumir la API desde el navegador con fetch, sin ningún framework.
  • Explicar qué es CORS, por qué lo aplica el navegador y por qué Postman no lo sufre.
  • Configurar CORS en el backend de forma explícita y acotada.
  • Diagnosticar un fallo de integración sabiendo si el problema es del cliente, del servidor o del navegador.