Leodario.com

Leodario.com – Tudo sobre Tecnologia

Aula 36 – API JavaScript, Node.js e Express – CORS Fundamentals – Cross-Origin Resource Sharing

Imagem destacada da aula de API

Introdução (3 min)

Olá, futuros arquitetos de sistemas! Sejam muito bem-vindos à AULA 36 do nosso intensivo em APIs. Hoje mergulharemos em um tema fundamental que é a espinha dorsal da segurança e interoperabilidade em APIs modernas: o CORS, ou Cross-Origin Resource Sharing.

Imagine o seguinte cenário: você está em uma cidade grande e decide visitar um clube noturno exclusivo. Ao chegar na entrada, o segurança pede sua identificação e verifica uma lista VIP. Se seu nome estiver lá, ou se você for um membro conhecido, a entrada é liberada. Caso contrário, por mais que você tente, o acesso é negado, e você não consegue sequer ver o que acontece lá dentro.

No mundo das APIs, o seu servidor backend é o “clube noturno exclusivo”. As aplicações frontend (websites, aplicativos móveis) são os “visitantes” que tentam acessar os recursos. O CORS é o “segurança” na porta, uma política de segurança implementada pelos navegadores web que define quem está na lista VIP e pode acessar os recursos do seu servidor. Sem ele, os navegadores, por padrão, bloqueiam o acesso entre diferentes “clubes” (origens) para proteger os usuários contra ataques maliciosos.

Por que isso é tão essencial para APIs modernas? No cenário atual, é incomum ter o frontend e o backend hospedados no mesmo domínio. Geralmente, uma aplicação frontend rodando em meusite.com precisa se comunicar com uma API hospedada em api.meusite.com ou até mesmo outrodominio.com. Sem uma configuração explícita de CORS, essa comunicação seria simplesmente impossibilitada, gerando erros frustrantes nos navegadores. Ele viabiliza a integração segura entre diferentes serviços e a construção de arquiteturas distribuídas.

Nesta aula, você aprenderá exatamente o que é CORS, como ele opera nos bastidores, e, o mais valioso, como implementá-lo de forma robusta e segura em suas APIs Node.js com Express. Vamos entender as configurações mais comuns, as melhores práticas corporativas e até como lidar com isso em ambientes de hospedagem como o HostGator Plano M.

Conceito Fundamental (7 min)

O Cross-Origin Resource Sharing (CORS) é um mecanismo de segurança implementado por navegadores web que permite que recursos restritos em uma página web sejam solicitados a partir de um domínio diferente daquele que serviu o primeiro recurso. Ele surge como uma flexibilização controlada da Same-Origin Policy (SOP) — a Política de Mesma Origem.

Terminologia Correta da Indústria

    • Origem (Origin): Uma combinação de esquema (protocolo, ex: http://, https://), host (domínio, ex: www.exemplo.com) e porta (ex: :8080). Duas URLs têm a mesma origem se todos esses três componentes forem idênticos.
    • Same-Origin Policy (SOP): Uma política de segurança fundamental do navegador que restringe como um documento ou script carregado de uma origem pode interagir com um recurso de outra origem. Por exemplo, um script em siteA.com não pode, por padrão, fazer uma requisição Ajax para siteB.com. O CORS vem para relaxar essa restrição de forma controlada.
    • Requisição Cross-Origin: Uma requisição HTTP feita por um navegador para uma URL que possui uma origem diferente da URL da página web atual.
    • Preflight Request: Para certas requisições cross-origin mais “complexas” (que incluem métodos HTTP diferentes de GET, POST para formulários simples, HEAD; que usam headers personalizados, ou Content-Type diferente de application/x-www-form-urlencoded, multipart/form-data ou text/plain), o navegador primeiro envia uma requisição OPTIONS, conhecida como “preflight”. Esta requisição serve para verificar com o servidor quais métodos e headers são permitidos para a origem solicitante.

Explicação Detalhada do Conceito Técnico

Quando um navegador detecta uma requisição cross-origin, ele adiciona um cabeçalho Origin com a origem da página solicitante. O servidor, por sua vez, deve responder com um cabeçalho Access-Control-Allow-Origin, indicando qual(is) origem(ns) têm permissão para acessar o recurso. Se o valor do Access-Control-Allow-Origin não corresponder à origem da requisição, ou se não houver tal cabeçalho, o navegador bloqueará a resposta, e a aplicação cliente receberá um erro.

No caso de uma Preflight Request, o navegador envia um método OPTIONS, incluindo cabeçalhos como Access-Control-Request-Method e Access-Control-Request-Headers. O servidor responde a essa requisição OPTIONS com cabeçalhos CORS que informam quais métodos (Access-Control-Allow-Methods), cabeçalhos (Access-Control-Allow-Headers) e tempo de cache (Access-Control-Max-Age) são permitidos. Somente após uma resposta positiva do preflight, o navegador envia a requisição HTTP real.

Casos de Uso Reais em Produção

    • Single-Page Applications (SPAs): Aplicativos frontend como React, Angular ou Vue.js, hospedados em um domínio (ex: app.minhaloja.com), que consomem APIs de backend hospedadas em outro (ex: api.minhaloja.com).
    • Microsserviços: Um serviço que precisa chamar outro serviço que está em uma origem diferente.
    • Integração com serviços de terceiros: Widgets ou scripts de terceiros que precisam fazer requisições para seus próprios servidores a partir do seu domínio.
    • APIs Públicas: Quando você desenvolve uma API para ser consumida por qualquer cliente, você geralmente permite CORS de todas as origens ().

Como isso se Integra com Outras Tecnologias

O CORS não é uma tecnologia isolada; ele é um contrato entre o navegador e o servidor. No Node.js com Express, ele é geralmente implementado através de middlewares, que interceptam as requisições e adicionam os cabeçalhos apropriados às respostas. Servidores web como Nginx ou Apache também podem ser configurados para adicionar cabeçalhos CORS antes que a requisição chegue à sua aplicação Node.js, agindo como um proxy reverso.

Vantagens e Desvantagens

    • Vantagens:
      • Segurança Aprimorada: Permite que os desenvolvedores definam explicitamente quais origens são permitidas a interagir com seus recursos, protegendo os usuários contra ataques de falsificação de requisição entre sites (CSRF) e outros ataques maliciosos.
      • Flexibilidade:Habilita a construção de arquiteturas modernas e distribuídas, onde frontend e backend podem residir em domínios distintos.
      • Padrão Aberto: É um padrão W3C, bem documentado e amplamente suportado por todos os navegadores modernos.
    • Desvantagens:
      • Complexidade de Configuração: Uma configuração inadequada pode levar a vulnerabilidades de segurança (se for muito permissivo) ou a problemas de funcionalidade (se for muito restritivo).
      • Erros Frustrantes: Os erros de CORS são notoriamente difíceis de depurar para iniciantes, pois aparecem no console do navegador e não diretamente no log do servidor.
      • Overhead para Preflight: Para cada requisição “complexa”, uma requisição OPTIONS adicional é feita, o que pode adicionar um pequeno overhead de rede, embora geralmente otimizado pelo cache do navegador.

Implementação Prática (10 min)

Agora é a hora de transformar teoria em prática. Vamos construir um servidor Express simples e configurar o CORS de várias maneiras, demonstrando as melhores práticas e considerando um ambiente como o HostGator Plano M.

Para começar, certifique-se de ter o Node.js instalado. Crie um novo diretório para o seu projeto e inicialize-o:


mkdir minha-api-cors
cd minha-api-cors
npm init -y
npm install express cors winston dotenv

O pacote cors é um middleware Express oficial que facilita imensamente a configuração do CORS. Usaremos winston para logging profissional e dotenv para gerenciar variáveis de ambiente.

Crie um arquivo chamado server.js (ou app.js) no diretório raiz do seu projeto:


// Importa os módulos essenciais
require('dotenv').config(); // Carrega variáveis de ambiente do arquivo .env
const express = require('express');
const cors = require('cors');
const winston = require('winston'); // Para logging profissional
const path = require('path');

// --- Configuração do Logger Profissional (Winston) --- // Define o formato do log para incluir timestamp, nível e mensagem const logFormat = winston.format.printf(({ level, message, timestamp }) => { return [${timestamp}] ${level.toUpperCase()}: ${message}; });

const logger = winston.createLogger({ level: 'info', // Nível mínimo de log a ser exibido format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), // Adiciona timestamp logFormat // Usa o formato personalizado ), transports: [ new winston.transports.Console(), // Exibe logs no console new winston.transports.File({ filename: 'error.log', level: 'error' }), // Logs de erro em arquivo new winston.transports.File({ filename: 'combined.log' }) // Todos os logs em outro arquivo ], });

// --- Inicialização do Servidor Express --- const app = express(); const PORT = process.env.PORT || 3000; // Define a porta, pegando do .env ou usando 3000 como fallback

// --- Configuração de CORS (Variantes) ---

// # Opção 1: CORS Totalmente Aberto (CUIDADO EM PRODUÇÃO!) // Permite requisições de QUALQUER origem. Útil para APIs públicas ou durante o desenvolvimento. // Descomente esta linha para usar esta opção. // app.use(cors()); // logger.warn('CORS configurado para permitir todas as origens (). CUIDADO em produção!');

// # Opção 2: CORS para Origens Específicas (MELHOR PRÁTICA PARA PRODUÇÃO) // Define uma lista de origens permitidas. // O ideal é que as origens sejam carregadas de variáveis de ambiente para facilitar a gestão. const allowedOrigins = process.env.CORS_ALLOWED_ORIGINS ? process.env.CORS_ALLOWED_ORIGINS.split(',') // Divide a string em um array : ['http://localhost:8080', 'http://127.0.0.1:5500']; // Fallback para desenvolvimento

const corsOptions = { origin: (origin, callback) => { // Verifica se a origem da requisição está na lista de origens permitidas. // Ou se a origem é indefinida (requisições de mesma origem, ou ferramentas como Postman/cURL) if (!origin || allowedOrigins.includes(origin)) { callback(null, true); // Permite a requisição logger.info(CORS: Requisição da origem '${origin || 'N/A'}' permitida.); } else { callback(new Error('Não permitido pelo CORS'), false); // Nega a requisição logger.warn(CORS: Requisição da origem '${origin}' NEGADA.); } }, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Métodos HTTP permitidos allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept'], // Headers permitidos credentials: true, // Habilita o envio de cookies e cabeçalhos de autorização em requisições cross-origin optionsSuccessStatus: 200 // Algumas ferramentas legadas esperam 200 para OPTIONS };

app.use(cors(corsOptions)); // Aplica as opções de CORS configuradas

// # Opção 3: CORS Manual (Para entendimento profundo) // Se você não quiser usar o pacote 'cors', pode implementar manualmente. // Isso é mais complexo e o pacote 'cors' é geralmente preferível. / app.use((req, res, next) => { const origin = req.headers.origin; if (allowedOrigins.includes(origin)) { res.setHeader('Access-Control-Allow-Origin', origin); } res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept'); res.setHeader('Access-Control-Allow-Credentials', 'true'); res.setHeader('Access-Control-Max-Age', '86400'); // Cache do preflight por 24 horas

// Lida com requisições preflight (OPTIONS) if (req.method === 'OPTIONS') { res.sendStatus(200); } else { next(); } }); logger.warn('CORS configurado manualmente. Certifique-se de que a lógica está correta!'); /

// --- Middleware para parsing de JSON no corpo das requisições --- app.use(express.json());

// --- Rotas da API (Exemplo) ---

// Rota raiz simples app.get('/', (req, res) => { logger.info('Requisição recebida na rota /'); res.send('API de CORS Rodando! Acesse /dados para ver os recursos.'); });

// Exemplo de dados (simulando um banco de dados) const produtos = [ { id: 1, nome: 'Notebook Gamer', preco: 7500.00 }, { id: 2, nome: 'Smartphone Ultra', preco: 3200.00 }, { id: 3, nome: 'Monitor Curvo', preco: 1800.00 } ];

// Rota para obter todos os produtos app.get('/dados', (req, res) => { logger.info('Requisição GET /dados recebida.'); res.status(200).json(produtos); });

// Rota para adicionar um novo produto (exige POST e Content-Type: application/json) app.post('/dados', (req, res) => { const { nome, preco } = req.body; // Validação de entrada robusta (exemplo simples) if (!nome || typeof nome !== 'string' || !preco || typeof preco !== 'number') { logger.error('Validação falhou: dados de produto inválidos.'); return res.status(400).json({ mensagem: 'Dados de produto inválidos. Nome e Preço são obrigatórios.' }); }

const novoProduto = { id: produtos.length + 1, nome, preco }; produtos.push(novoProduto); logger.info(Produto adicionado: ${JSON.stringify(novoProduto)}); res.status(201).json(novoProduto); });

// --- Middleware de tratamento de erros global --- app.use((err, req, res, next) => { logger.error(Erro inesperado: ${err.stack}); res.status(500).json({ mensagem: 'Ocorreu um erro interno no servidor.' }); });

// --- Inicialização do Servidor --- app.listen(PORT, () => { logger.info(Servidor API rodando na porta ${PORT}); logger.info(Acesse: http://localhost:${PORT}); logger.info(Origens CORS permitidas: ${allowedOrigins.join(', ')}); });

Crie um arquivo .env na raiz do projeto para suas variáveis de ambiente:


PORT=3001
CORS_ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:5500,https://seucliente.com,https://outrocliente.io

Comentários Detalhados Linha por Linha

    • require('dotenv').config();: Carrega as variáveis definidas no arquivo .env para process.env, mantendo segredos e configurações fora do código fonte.
    • Winston Logger: Configura um logger robusto que registra informações no console e em arquivos (error.log para erros, combined.log para todos os logs). Isso é um padrão enterprise para monitoramento.
    • const app = express();: Inicializa a aplicação Express.
    • const PORT = process.env.PORT || 3000;: Define a porta de execução, preferencialmente via variável de ambiente.
    • CORS_ALLOWED_ORIGINS: No .env, definimos uma lista de origens separadas por vírgula. O código converte essa string em um array. Esta é a maneira mais segura e flexível de gerenciar as origens permitidas em produção.
    • corsOptions: Objeto de configuração detalhado para o middleware cors:
      • origin: Uma função de callback que verifica dinamicamente a origem da requisição. Se a origem estiver na lista allowedOrigins ou for uma requisição sem origem (como cURL), ela é permitida. Caso contrário, é bloqueada. Este é o controle mais granular e recomendado.
      • methods: Especifica os métodos HTTP (GET, POST, PUT, DELETE, OPTIONS) que são permitidos para requisições cross-origin.
      • allowedHeaders: Define quais cabeçalhos HTTP personalizados ou não-padrão são permitidos nas requisições cross-origin. Authorization é vital para APIs que usam tokens JWT, por exemplo.
      • credentials: true: Permite que o navegador inclua cookies, cabeçalhos de autorização HTTP ou certificados TLS em requisições cross-origin. Se você usa autenticação baseada em sessão ou JWT via cookies, isso é essencial. O cliente também deve configurar withCredentials = true.
      • optionsSuccessStatus: 200: Define o status de sucesso para requisições preflight (OPTIONS).
    • app.use(cors(corsOptions));: Aplica o middleware cors com as opções definidas globalmente para todas as rotas.
    • CORS Manual (comentado): Incluí um bloco comentado mostrando como você faria a configuração de CORS “na mão”, sem o pacote cors. É bom para entender os cabeçalhos subjacentes, mas o pacote é preferível para a maioria dos cenários devido à sua simplicidade e robustez.
    • app.use(express.json());: Middleware para parsear o corpo das requisições com JSON.
    • Rotas de Exemplo: As rotas /dados demonstram uma API RESTful simples, simulando operações de banco de dados.
    • Validação de Entrada: Um exemplo básico de validação na rota POST /dados. Em um sistema enterprise, isso seria muito mais robusto, talvez com bibliotecas como Joi ou Yup.
    • Middleware de Erros: Um handler global para capturar e logar erros inesperados, retornando uma mensagem genérica ao cliente.
    • app.listen(...): Inicia o servidor e registra mensagens informativas no log.

Configurações Específicas para HostGator Plano M

O HostGator Plano M é uma hospedagem compartilhada, predominantemente configurada para PHP. Rodar Node.js diretamente em um ambiente como o “Plano M” tradicional pode ser um desafio, pois ele não oferece suporte nativo e flexível para processos Node.js de longa duração ou gerenciadores de processos como PM2.

No entanto, se você consegue instalar e executar Node.js via SSH (que alguns planos compartilhados da HostGator podem permitir), a configuração de CORS permanece a mesma dentro do seu código Node.js. O que importa é que sua aplicação Express receba as requisições e tenha a chance de processar os cabeçalhos CORS.

Pontos a considerar no HostGator (ou ambiente similar):

    • Variáveis de Ambiente: Garanta que as variáveis CORS_ALLOWED_ORIGINS e PORT sejam configuradas no ambiente onde seu Node.js está sendo executado. Geralmente, isso é feito através de arquivos .htaccess (se o Apache for o proxy) ou diretamente no script de inicialização do seu processo Node.js. Por exemplo, em .htaccess você poderia adicionar: SetEnv CORS_ALLOWED_ORIGINS "https://seusite.com,https://outro.com".
    • Proxy Reverso (Apache/Nginx): Em muitos ambientes de hospedagem compartilhada, o Apache ou Nginx atua como um proxy reverso para aplicações Node.js. Nesses casos, o servidor web (Apache/Nginx) também pode ser configurado para adicionar cabeçalhos CORS. Se você tiver problemas com CORS mesmo com a configuração no Express, verifique se há alguma configuração de CORS no nível do servidor web que esteja sobrescrevendo ou interferindo. Em geral, é melhor que sua aplicação Node.js seja a fonte única da verdade para CORS.
    • Porta: Em hospedagens compartilhadas, você pode não ter controle sobre a porta. Sua aplicação Node.js pode precisar ouvir em uma porta arbitrária fornecida pelo ambiente (ex: via uma variável de ambiente como process.env.PORT) e ser acessada via um proxy reverso configurado para a porta 80/443.

Error Handling Otimizado

Nosso logger com Winston possibilita a captura e persistência de erros, o que é crucial em produção. O middleware de erro global (app.use((err, req, res, next) => {...})) garante que qualquer erro não tratado dentro das rotas seja capturado, logado, e uma resposta padronizada seja enviada ao cliente, evitando vazamento de informações sensíveis.

Testes Básicos Incluídos

Para testar o servidor, execute-o:


node server.js

Você pode testar as rotas usando curl (ignora CORS) ou um cliente HTTP como o Postman/Insomnia. O verdadeiro teste de CORS se dá no navegador.


Teste GET (deve funcionar)

📚 Informações da Aula

Curso: API Completo - Node.js & Express

Tempo estimado: 25 minutos

Pré-requisitos: JavaScript básico

curl http://localhost:3001/dados

Teste POST (com Content-Type JSON, simula preflight)

curl -X POST -H "Content-Type: application/json" -d '{"nome":"Teclado Mecanico","preco":450.00}' http://localhost:3001/dados

Para o teste de navegador, crie um arquivo cliente.html em qualquer lugar, por exemplo na área de trabalho, e abra-o com Live Server (extensão do VS Code) ou similar, que irá servi-lo em http://127.0.0.1:5500 ou http://localhost:8080 (dependendo da sua variável CORS_ALLOWED_ORIGINS):

Teste de CORS com API Node.js

Cliente para Teste de CORS

Este cliente está rodando em http://127.0.0.1:5500 (ou similar), que deve ser uma origem permitida no seu servidor Node.js.

Resposta da API:

Aguardando requisição...


Exercício Hands-On (5 min)

Para solidificar seu conhecimento, vamos a um desafio prático.

Desafio prático para implementar sozinho

Modifique a API existente para:

    • Adicionar uma nova rota /relatorios que apenas permita requisições GET e de uma origem específica: https://dashboard.seuexemplo.com.
    • Garantir que a rota /dados aceite PUT para atualizar um produto existente, mas apenas da origem https://editor.seuexemplo.com.
    • Incluir um cabeçalho customizado X-Api-Key nas requisições POST e PUT para ambas as rotas.
    • Adicionar um logging detalhado no servidor para cada requisição CORS, informando a origem, o método e se foi permitida ou negada.

Solução detalhada passo a passo

Primeiro, atualize seu arquivo .env com as novas origens permitidas:


PORT=3001
CORS_ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:5500,https://seucliente.com,https://outrocliente.io,https://dashboard.seuexemplo.com,https://editor.seuexemplo.com

Agora, modifique o arquivo server.js:


// ... (imports e configuração do logger permanecem os mesmos) ...

// --- Configuração de CORS (Variantes) --- const allowedOrigins = process.env.CORS_ALLOWED_ORIGINS ? process.env.CORS_ALLOWED_ORIGINS.split(',') : ['http://localhost:8080', 'http://127.0.0.1:5500'];

// Função auxiliar para verificar a origem e logar const checkCorsOrigin = (origin, callback, specificAllowedOrigins = []) => { const combinedAllowed = [...allowedOrigins, ...specificAllowedOrigins]; if (!origin || combinedAllowed.includes(origin)) { logger.info(CORS: Requisição da origem '${origin || 'N/A'}' permitida. (Rotas padrão/específicas)); callback(null, true); } else { logger.warn(CORS: Requisição da origem '${origin}' NEGADA. (Rotas padrão/específicas)); callback(new Error('Não permitido pelo CORS'), false); } };

// Opções CORS globais para a maioria das rotas const defaultCorsOptions = { origin: (origin, callback) => checkCorsOrigin(origin, callback), methods: ['GET', 'POST', 'OPTIONS'], // Limitei para fins de demonstração que PUT virá depois allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept', 'X-Api-Key'], credentials: true, optionsSuccessStatus: 200 }; app.use(cors(defaultCorsOptions)); // Aplica opções padrão

// --- Middleware para parsing de JSON no corpo das requisições --- app.use(express.json());

// --- Rotas da API (Exemplo) ---

// ... (rota raiz e produtos permanecem os mesmos) ...

// Rota para obter todos os produtos app.get('/dados', (req, res) => { logger.info('Requisição GET /dados recebida.'); res.status(200).json(produtos); });

// Rota para adicionar um novo produto app.post('/dados', (req, res) => { logger.info(Requisição POST /dados recebida da origem: ${req.headers.origin}. X-Api-Key: ${req.headers['x-api-key'] || 'N/A'}); const { nome, preco } = req.body; if (!nome || typeof nome !== 'string' || !preco || typeof preco !== 'number') { logger.error('Validação falhou: dados de produto inválidos.'); return res.status(400).json({ mensagem: 'Dados de produto inválidos. Nome e Preço são obrigatórios.' }); }

const novoProduto = { id: produtos.length + 1, nome, preco }; produtos.push(novoNovoProduto); logger.info(Produto adicionado: ${JSON.stringify(novoNovoProduto)}); res.status(201).json(novoNovoProduto); });

// --- Rota PUT para /dados/:id com CORS específico --- // Para esta rota, vamos usar um middleware CORS local para sobrescrever as opções globais const editorCorsOptions = { origin: (origin, callback) => checkCorsOrigin(origin, callback, ['https://editor.seuexemplo.com']), methods: ['PUT', 'OPTIONS'], // Apenas PUT e OPTIONS são permitidos allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'Accept', 'X-Api-Key'], credentials: true, optionsSuccessStatus: 200 };

app.put('/dados/:id', cors(editorCorsOptions), (req, res) => { logger.info(Requisição PUT /dados/:id recebida da origem: ${req.headers.origin}. X-Api-Key: ${req.headers['x-api-key'] || 'N/A'}); const { id } = req.params; const { nome, preco } = req.body;

const produtoIndex = produtos.findIndex(p => p.id === parseInt(id));

if (produtoIndex === -1) { logger.warn(Produto com ID ${id} não encontrado para atualização.); return res.status(404).json({ mensagem: 'Produto não encontrado.' }); }

// Validação de entrada if (!nome || typeof nome !== 'string' || !preco || typeof preco !== 'number') { logger.error('Validação falhou: dados de produto inválidos para atualização.'); return res.status(400).json({ mensagem: 'Dados de produto inválidos. Nome e Preço são obrigatórios.' }); }

produtos[produtoIndex] = { ...produtos[produtoIndex], nome, preco }; logger.info(Produto com ID ${id} atualizado para: ${JSON.stringify(produtos[produtoIndex])}); res.status(200).json(produtos[produtoIndex]); });

// --- Nova rota /relatorios com CORS específico --- const dashboardCorsOptions = { origin: (origin, callback) => checkCorsOrigin(origin, callback, ['https://dashboard.seuexemplo.com']), methods: ['GET', 'OPTIONS'], // Apenas GET e OPTIONS são permitidos allowedHeaders: ['Authorization'], // Apenas Authorization é permitido para relatórios credentials: true, optionsSuccessStatus: 200 };

app.get('/relatorios', cors(dashboardCorsOptions), (req, res) => { logger.info(Requisição GET /relatorios recebida da origem: ${req.headers.origin}.); // Simula a busca de dados de relatório const relatorio = { totalVendas: 150000.00, clientesAtivos: 1200, ultimasTransacoes: [ { id: 'TRX001', valor: 250.00, data: '2023-10-26' }, { id: 'TRX002', valor: 1500.00, data: '2023-10-25' } ] }; res.status(200).json(relatorio); });

// ... (middleware de tratamento de erros global e inicialização do servidor permanecem os mesmos) ...

Como testar e validar o resultado

    • Reinicie seu servidor Node.js com node server.js.
    • Para /relatorios:
      • Tente acessar http://localhost:3001/relatorios de um navegador ou do cliente.html. Ele deve ser bloqueado, pois http://localhost ou http://127.0.0.1:5500 não estão na lista de origens permitidas especificamente para esta rota (apenas https://dashboard.seuexemplo.com). Você verá um erro de CORS no console do navegador.
      • Para um teste “bem-sucedido” de /relatorios, você precisaria de um cliente JavaScript rodando em https://dashboard.seuexemplo.com. Para simular, você pode adicionar temporariamente http://localhost:8080 (ou a porta do seu Live Server) à lista de origens para dashboardCorsOptions e tentar novamente.
    • Para PUT /dados/:id:
      • Modifique seu cliente.html para incluir um botão que faça uma requisição PUT com um cabeçalho X-Api-Key.
      • Tente fazer um PUT de http://localhost:8080 (ou a porta do seu Live Server). Ele deve ser bloqueado, a menos que você adicione esta origem à lista de editorCorsOptions.
      • Tente com curl:
        
        curl -X PUT -H "Content-Type: application/json" -H "X-Api-Key: meu_segredo" -d '{"nome":"Mouse Ultra Atualizado","preco":250.00}' http://localhost:3001/dados/1
                        

        (Este curl irá ignorar a política de CORS, mas você verá o log no servidor com o X-Api-Key).

    • Verifique os Logs: No seu terminal onde o servidor está rodando, observe os logs do Winston. Você verá mensagens claras sobre qual origem tentou acessar qual rota e se foi permitida ou negada, juntamente com o cabeçalho X-Api-Key.

Troubleshooting dos erros mais comuns

    • CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.: O erro mais comum. Significa que a origem do seu cliente não está na lista de origens permitidas no seu servidor, ou o middleware CORS não foi aplicado corretamente na rota acessada. Verifique CORS_ALLOWED_ORIGINS no seu .env e as configurações de corsOptions.
    • Requisições OPTIONS falhando: Se você está vendo erros com métodos OPTIONS, significa que o servidor não está respondendo corretamente às requisições preflight. Verifique se app.use(cors(corsOptions)) está antes de suas rotas e se os métodos (incluindo OPTIONS) e cabeçalhos permitidos estão corretos no seu corsOptions.
    • The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '' when the request's credentials mode is 'include'.: Você está usando credentials: true no seu corsOptions, mas a origem permitida está definida como *. Para permitir credenciais, você deve especificar origens explícitas ou usar uma função que valida dinamicamente, como fizemos.
    • Cabeçalhos personalizados bloqueados: Se um cabeçalho como Authorization ou X-Api-Key não está sendo enviado, verifique se ele está incluído na lista allowedHeaders em seu corsOptions.

Próximos passos sugeridos

Continue explorando!

    • Pesquise sobre como configurar CORS em servidores proxy como Nginx ou Apache, caso sua infraestrutura use um.
    • Aprenda sobre segurança de APIs, incluindo autenticação (JWT, OAuth2) e autorização, que frequentemente dependem de CORS para funcionar corretamente em cenários cross-origin.
    • Implemente um ambiente de desenvolvimento e produção para suas variáveis de ambiente (ex: .env.development, .env.production) para gerenciar as origens permitidas de forma mais granular.
    • Considere adicionar um gerenciador de configurações mais robusto, como config ou nconf, para cenários enterprise complexos.

🚀 Pronto para a próxima aula?

Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!

📚 Ver todas as aulas