BrasilAPI / cep-promise

Busca por CEP integrado diretamente aos serviços dos Correios, ViaCEP e outros (Node.js e Browser)
MIT License
2.88k stars 316 forks source link

Fetch cors para cdn da apicep.com #247

Closed rafas closed 2 years ago

rafas commented 2 years ago

Olá pessoal,

Estamos fechando o acesso público ao ws.apicep.com e pra não deixar a comunidade sem acesso, criamos uma alternativa que oferece exatamente os mesmos dados, mas baseado em um CDN de arquivos estáticos atualizados de forma assíncrona.

Nossa proposta pro webservice antigo é buscar os dados do CEP em tempo real caso ele esteja desatualizado ou não exista em nossa base, porém devido ao volume gigantesco de pesquisas de CEPs inválidos (tentativas de dump forçado, algo assim) nossa fila de atualizações fica quase sempre prejudicada (por isso as restrições de flood vindos da mesma origem).

Enfim, essa issue está sendo aberta por causa de um problema com o cors. Nós deixamos o CORS do CDN totalmente aberto pra poder ser acessado em todos os sites e servidores, porém como ele está baseado em block storage (s3), o funcionamento do cabeçalho parece ser um pouco diferente. Ao invés da liberação do CORS ficar setada para * o servidor S3 parece especificar nominalmente o domínio de origem da consulta. Ou seja, se você faz o fetch a partir do abcd.com o header da resposta vem assim: access-control-allow-origin: https://abcd.com e não assim: access-control-allow-origin: * A princípio isso não era pra ser um problema, mas quando o fetch é feito com o mode cors, o preflight do navegador da erro. Curiosamente, quando eu faço fetch sem definir as options, o navegador permite o carregamento normalmente em qualquer domínio.

Alguém pode ajudar nessa questão? URL para teste https://cdn.apicep.com/file/apicep/64009-140.json

murilohns commented 2 years ago

@rafas você consegue nos instruir em como simular esse problema?

Eu coloquei esse endpoint no cep-promise e testei pelo repositório do Brasil API, tá retornando certinho

image image

Pelo navegador direto também deu certo

image

Se puder me ajudar a simular esse problema, agradeço

rafas commented 2 years ago

@murilohns obrigado pela resposta

Acho que esse erro está acontecendo só em consultas a partir do navegador. Eu não cheguei a testar com a biblioteca em si, mas peguei o trecho de código que é usado na chamada pra testar em hardcode direto no console do chrome.

O trecho de código seria esse

fetch('https://cdn.apicep.com/file/apicep/64009-140.json', {
    method: 'GET',
    mode: 'cors',
    headers: {
      'content-type': 'application/json;charset=utf-8'
    },
    timeout: 30000
  }).then(res => res.json()).then(json => console.log(json))

O erro que retornou aqui é esse: Erro na consulta do cep com cors

Porém se a consulta é feita sem as options, o resultado sai certinho

fetch('https://cdn.apicep.com/file/apicep/64009-140.json').then(res => res.json()).then(json => console.log(json))

Resposta sem cors

Debug

Fiz alguns testes usando o insomnia, mas replicando os mesmos cabeçalhos que retornaram erro la no navegador, e identifiquei que quando o mode: cors está ativado no fetch, ele usa esses cabeçalhos específicos na request:

access-control-request-headers: content-type;
access-control-request-method: GET;

E quando o fetch é feito sem o mode: cors, esses cabeçalhos não estão na request. Porém parece que o servidor está reagindo de forma diferente por causa do primeiro cabeçalho especificamente.

Veja o exemplo abaixo:

Primeiro, simulando a chamada sem o mode: cors Exemplo de chamada sem mode cors

Depois, simulando a chamada com o mode: cors Exemplo de chamada com mode cors

Acho que isso só afeta o uso da biblioteca rodando no front-end, mas pelo que temos visto nos logs de acesso da api antiga, tem muitos projetos consultando a partir do navegador.

rafas commented 2 years ago

Acho que identifiquei o problema. Fiz mais alguns testes, e percebi que a diferença do cabeçalho de resposta citado na mensagem anterior, não tem a ver com o mode: cors mas sim com o header content-type sendo setado na request, e não na response. Posso estar falando bobagem, mas acho que no header da request é setado o accept: application/json (sem charset) enquanto o content-type é setado apenas no response.

Enfim, essa anomalia tem mais a ver com o comportamento do servidor S3, mas causado pela presença de um cabeçalho que não deveria estar na request.

Segue o exemplo de como funcionou.. Poderia deixar o header vazio também, mas pra manter o padrão acho que é melhor manter o accept..

fetch('https://cdn.apicep.com/file/apicep/64009-140.json', {
    method: 'GET',
    mode: 'cors',
    headers: {
        accept: 'application/json'
    },
    timeout: 30000
  }).then(res => res.json()).then(json => console.log(json))

Resultado Resultado sem header errado

https://github.com/BrasilAPI/cep-promise/pull/248

LorhanSohaky commented 2 years ago

Acho que identifiquei o problema. Fiz mais alguns testes, e percebi que a diferença do cabeçalho de resposta citado na mensagem anterior, não tem a ver com o mode: cors mas sim com o header content-type sendo setado na request, e não na response. Posso estar falando bobagem, mas acho que no header da request é setado o accept: application/json (sem charset) enquanto o content-type é setado apenas no response.

Enfim, essa anomalia tem mais a ver com o comportamento do servidor S3, mas causado pela presença de um cabeçalho que não deveria estar na request.

Segue o exemplo de como funcionou.. Poderia deixar o header vazio também, mas pra manter o padrão acho que é melhor manter o accept..

fetch('https://cdn.apicep.com/file/apicep/64009-140.json', {
    method: 'GET',
    mode: 'cors',
    headers: {
        accept: 'application/json'
    },
    timeout: 30000
  }).then(res => res.json()).then(json => console.log(json))

Resultado Resultado sem header errado

248

Quando vocês estão salvando o arquivo no s3 estão setando o content type?

LorhanSohaky commented 2 years ago

Acredito que seja possível também especificar o charset

rafas commented 2 years ago

@LorhanSohaky Sim, o erro parece ser no fetch mesmo.. O header content-type só é usado em requests de POST ou PUT pra especificar o conteúdo do body. No contexto de GET se usa apenas o accept para especificar qual tipo de resposta vc espera do servidor.

Usando o accept, o servidor responde corretamente com o access-control-allow-origin especificando o domínio de origem da solicitação.