A evolução da arquitetura web: a nostalgia do PHP e o pesadelo do CORS

“Naquela época, um arquivo só bastava…”

O PHP com que eu trabalhava na faculdade era realmente confortável. Em um único arquivo index.php, dava para colocar as tags HTML, abrir <?php ?> no meio e escrever ali mesmo o código de consulta ao banco de dados. Não existia uma separação clara entre frontend e backend. Bastava declarar uma variável e você podia usá-la em qualquer parte do HTML, e praticamente não havia erros causados por comunicação de dados.

Mas quando, na prática, mudei para uma stack com Vue.js no frontend e Spring Boot no backend, o inferno começou.

“Eu subi o servidor Spring localmente, a tela do Vue também está funcionando, então por que os dados não chegam?”

O console das ferramentas de desenvolvedor do Chrome estava coberto de mensagens de erro em vermelho vivo.

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

Afinal, o que é esse tal de CORS que insiste em bloquear meus dados? E por que abandonamos aquele jeito antigo e confortável, em que um único arquivo resolvia tudo, para separar de propósito frontend e backend?

Antes todos viviam sob o mesmo teto; agora moram em casas diferentes e precisam passar por uma inspeção.

Quem deve cozinhar? (SSR vs CSR)

Para entender essa mudança, primeiro é preciso entender quem prepara o “prato” chamado página web, ou seja, o HTML. É exatamente aí que está a diferença entre SSR (Server Side Rendering) e CSR (Client Side Rendering).

1. SSR: uma marmita pronta (PHP, JSP)

  • Como funciona: o servidor (a cozinha) prepara o arroz, os acompanhamentos e a sopa, arruma tudo caprichado na marmita (HTML) e envia.
  • Cliente (navegador): só precisa abrir a tampa da marmita e mostrar o que tem dentro.
  • Vantagens: a primeira tela carrega rápido. Os mecanismos de busca (SEO) conseguem ler bem o conteúdo.
  • Desvantagens: para trocar um único acompanhamento, é preciso receber a marmita inteira de novo. (a tela pisca)

2. CSR: um meal kit entregue em casa (React, Vue)

  • Como funciona: o servidor não cozinha. Ele só envia os ingredientes (dados JSON) e a receita (JavaScript).
  • Cliente (navegador): pega os ingredientes recebidos e frita, refoga e monta a tela ali mesmo, no meu computador (navegador).
  • Vantagens: dá para trocar só os acompanhamentos necessários, então as transições de tela ficam extremamente suaves (parece um app). A carga sobre o servidor diminui.
  • Desvantagens: no começo é preciso baixar a receita (arquivos JS), por isso o carregamento inicial é lento.

[Tip] CSR e SPA não são exatamente a mesma coisa

Os dois termos costumam ser usados como se fossem sinônimos, mas, tecnicamente, não são.

  • CSR (Client Side Rendering): é o “como” (How). Refere-se à forma como o navegador desenha a tela.
  • SPA (Single Page Application): é o “o quê” (What). Refere-se a uma aplicação que tem uma única página.

Normalmente usamos CSR para construir uma SPA. Em outras palavras: como queremos evitar o piscar causado pelo recarregamento completo da página usando uma única página (SPA), escolhemos uma abordagem em que o próprio navegador redesenha as partes necessárias (CSR).

Usamos Vue.js ou React por causa da experiência do usuário. Para fazer um site se comportar com a fluidez de um app de smartphone, transferimos o ato de “cozinhar” do servidor para o navegador.

CORS, o segurança que barra os visitantes indesejados

Quando frontend (Vue) e backend (Spring) se separam, na prática passamos a ter “duas casas”.

  • Casa do frontend: localhost:8080 (servidor Vue)
  • Casa do backend: localhost:3000 (servidor Spring)

E é aí que o problema aparece. Por razões de segurança, o navegador segue uma regra básica: “não confie com facilidade em recursos vindos de outra Origin”. Isso é a SOP, a Same Origin Policy.

Pense nisso. Eu estou logado no Naver, e um site criado por um hacker envia escondido uma requisição para o servidor do Naver dizendo: “me entregue os dados pessoais deste usuário”. Se o navegador não bloqueasse isso, todas as minhas informações ficariam expostas.

Por isso, o navegador bloqueia por padrão requisições quando a origem, ou seja, domínio ou porta, é diferente. Mas, para desenvolvimento, nós deliberadamente separamos as portas. Só queremos usar nossos próprios dados, e mesmo assim o navegador, como um segurança, responde: “Sua porta não bate. Entrada proibida.” Essa é a verdadeira natureza de um erro de CORS, isto é, Cross-Origin Resource Sharing.

[Code Verification] Emitindo um passe de entrada

A forma de resolver isso é, na verdade, simples. O servidor, ou seja, o backend, só precisa escrever um passe para o navegador, o segurança, dizendo: “Essa pessoa é um convidado que eu mesmo convidei. Pode deixar entrar.”

No Spring Boot, esse passe pode ser emitido com um único arquivo de configuração.

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 as rotas da API
                .allowedOrigins("http://localhost:8080") // 2. Permitir requisicoes desta origem
                .allowedMethods("GET", "POST", "PUT", "DELETE") // 3. Permitir esses tipos de requisicao
                .allowCredentials(true); // 4. Cookies e credenciais podem ser incluidos
    }
}

Análise:

  1. allowedOrigins: é o mais importante. Se você escrever aqui o endereço do servidor frontend (localhost:8080), o servidor Spring envia a resposta com o carimbo Access-Control-Allow-Origin: http://localhost:8080 no cabeçalho.
  2. O navegador confere esse carimbo, pensa “Ah, o dono do servidor autorizou” e entrega os dados ao frontend.

O ponto de atenção é o seguinte: por preguiça, não abra tudo com allowedOrigins("*"). Isso seria o mesmo que deixar a porta de casa escancarada com uma placa dizendo: “Ladrões, sejam bem-vindos”.

Conselho prático: quando usar o quê?

Então CSR, ou seja, Vue ou React, é sempre a resposta certa? Não. Na prática, a escolha depende do objetivo.

  1. Páginas de administração (Admin), dashboards:
    • Recomendação: CSR (Vue, React)
    • Motivo: há muita interação com dados, e não é preciso aparecer em mecanismos de busca. O que importa é uma resposta ágil, como a de um app.
  2. Sites de notícias, blogs, página inicial de lojas online:
    • Recomendação: SSR (Next.js, Nuxt.js)
    • Motivo: precisam aparecer bem nas buscas do Google (SEO), e, quando o cliente entra, o conteúdo deve surgir na hora, em vez de uma tela em branco.
  3. Pequenos projetos pessoais:
    • Recomendação: Thymeleaf (SSR embutido no Spring Boot)
    • Motivo: se você desenvolve sozinho, não vale a pena se desgastar separando frontend e backend e configurando CORS. Desenvolver tudo junto, como na época do PHP, é o que dá mais produtividade.

Fechando: cruzando a fronteira

A antiga “grande família sob o mesmo teto” da era do PHP era confortável, mas, quanto mais tudo se tornava complexo, mais difícil ficava administrar. O modelo atual, com “duas famílias separadas” entre frontend e backend, é mais incômodo por causa de comunicação e CORS, mas permite que cada lado se concentre melhor no seu papel.

Então, da próxima vez que você topar com um erro de CORS, tente não reagir só com irritação. O navegador não está te perseguindo; ele apenas está fazendo uma inspeção rigorosa para proteger a sua casa, isto é, o seu servidor.

Agora o caminho entre frontend e backend está aberto. Os dados podem ir e vir. Mas onde, afinal, esses dados ficam armazenados no servidor? Antigamente bastava disparar uma query e buscar o que fosse necessário. No Spring, porém, coisas como JPA e Entity dão a impressão de que escrever SQL diretamente já não é mais permitido.

Na próxima vez, vamos ver como a “forma de manual” de projetar bancos de dados, a normalização, pode na prática acabar atrapalhando e por que o JPA tenta tanto esconder o SQL de você.

Deixe um comentário