La evolución de la arquitectura web: la nostalgia de PHP y la pesadilla de CORS

📖 12min read

«En aquella época, con un solo archivo bastaba…»

El PHP con el que trabajé en la universidad era realmente cómodo. En un solo archivo index.php podías meter las etiquetas HTML, abrir <?php ?> entre ellas y escribir también el código de consulta a la base de datos. No había una separación clara entre frontend y backend. Bastaba con declarar una variable para poder usarla en cualquier parte del HTML, y prácticamente nunca aparecían errores por la comunicación de datos.

Pero en cuanto en el trabajo cambié a una pila tecnológica con Vue.js en el frontend y Spring Boot en el backend, empezó el infierno.

«He levantado el servidor de Spring en local, la pantalla de Vue también está funcionando, ¿entonces por qué no llegan los datos?»

La consola de las herramientas de desarrollo de Chrome estaba completamente cubierta de mensajes de error en rojo intenso.

Access to XMLHttpRequest at ... has been blocked by CORS policy

¿Qué demonios es ese «CORS» que bloquea mis datos? ¿Y por qué dejamos atrás aquel método tan cómodo en el que con un solo archivo bastaba, para esforzarnos en separar a propósito el frontend y el backend?

Antes vivían bajo el mismo techo; ahora viven en casas distintas y tienen que pasar por un control de seguridad.

¿Quién cocina? (SSR vs CSR)

Para entender este cambio, primero hay que entender quién prepara el «plato» llamado página web, es decir, el HTML. Esa es precisamente la diferencia entre SSR (Server Side Rendering) y CSR (Client Side Rendering).

1. SSR: un almuerzo ya preparado (PHP, JSP)

2. CSR: un kit de comida a domicilio (React, Vue)

[Tip] CSR y SPA no son exactamente lo mismo

A menudo se usan como si fueran sinónimos, pero en sentido estricto no lo son.

Normalmente usamos CSR para construir una SPA. Es decir, como queremos evitar el parpadeo de recargar páginas usando una sola página (SPA), elegimos un enfoque en el que el propio navegador redibuja las partes necesarias (CSR).

La razón por la que usamos Vue.js o React es la experiencia de usuario. Para que un sitio web funcione con la suavidad de una aplicación móvil, trasladamos el acto de «cocinar» del servidor al navegador.

CORS, el guardia de seguridad que detiene a los intrusos

Al separar el frontend (Vue) y el backend (Spring), terminamos teniendo «dos casas».

Y ahí empieza el problema. Por razones de seguridad, el navegador web tiene una regla básica: «no confíes a la ligera en recursos que vienen de otro Origin». Eso es la SOP, la Same Origin Policy.

Piénsalo así. Yo estoy conectado en Naver, y desde un sitio creado por un hacker se envía en secreto una petición al servidor de Naver diciendo: «dame la información personal de este usuario». Si el navegador no bloqueara eso, todos mis datos quedarían expuestos.

Por eso el navegador bloquea por defecto cualquier petición cuando el origen, es decir, el dominio o el puerto, es distinto. Pero nosotros, para desarrollar, dividimos deliberadamente los puertos. Solo queremos usar nuestros propios datos, y el navegador, como si fuera un guardia, nos dice: «Tu puerto no coincide. Prohibido el paso». Esa es la verdadera naturaleza del error CORS (Cross-Origin Resource Sharing).

[Code Verification] Emitir un pase de acceso

La forma de resolverlo es simple. El servidor, es decir, el backend, solo tiene que escribirle un pase al navegador, el guardia, diciéndole: «esta persona es un invitado mío, déjala entrar».

En Spring Boot, ese pase puede emitirse con un solo archivo de configuración.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 1. Para todas las rutas de la API
                .allowedOrigins("http://localhost:8080") // 2. Permitir solicitudes desde esta direccion
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 3. Permitir este tipo de solicitudes
                .allowCredentials(true); // 4. Se permiten cookies y credenciales
    }
}

Análisis:

Lo importante es no caer en la pereza y abrir todo usando allowedOrigins("*"). Eso sería como dejar la puerta de casa completamente abierta con un cartel que diga: «Bienvenidos, ladrones».

Consejo práctico: ¿cuándo usar qué?

Entonces, ¿significa eso que CSR, es decir, Vue o React, es siempre la respuesta correcta? No. En la práctica, hay que elegir según el objetivo.

Cierre: cruzar la frontera

Aquella «gran familia bajo un mismo techo» de la época de PHP era cómoda, pero cuanto más crecía la complejidad, más difícil se volvía de mantener. El modelo actual, dos familias separadas, frontend y backend, es más molesto por cosas como CORS, pero permite que cada lado se concentre en su propio papel.

Así que la próxima vez que te encuentres con un error de CORS, intenta no reaccionar solo con enfado. El navegador no te está fastidiando: simplemente está haciendo una inspección estricta para proteger tu casa, es decir, tu servidor.

Ahora el camino entre frontend y backend ya está abierto. Los datos pueden ir y venir. Pero ¿dónde se almacenan realmente esos datos dentro del servidor? Antes bastaba con lanzar una consulta y traerlos. En Spring, en cambio, aparecen cosas como JPA y Entity, y da la sensación de que ya no te dejan escribir SQL directamente.

La próxima vez veremos cómo la «forma canónica» del diseño de bases de datos, la normalización, puede convertirse en un obstáculo en la práctica, y por qué JPA insiste tanto en ocultarte el SQL.

Deja un comentario