O formulário é enviado, o indicador aparece e o console diz que há um token < inesperado em JSON na posição 0. A própria página carrega bem, e é por isso que o primeiro impulso é culpar o analisador ou o túnel. Normalmente nenhum dos dois está envolvido: o envio é uma requisição diferente do carregamento da página, e voltou com algo que não é JSON. Trabalhe na ordem em que o navegador realmente revela isso, porque as quatro respostas possíveis exigem quatro correções diferentes.

Passo 1: capture o envio que realmente aconteceu

Abra o painel Rede antes de enviar, deixe o registro configurado para preservar entradas entre navegações e faça um envio de teste inofensivo numa conta que você tenha permissão de usar. Selecione a requisição que o próprio formulário iniciou, não a requisição de documento da página.

Cuidado com filtros. Um filtro XHR ou Fetch é prático, mas se o framework ainda envia por um iframe oculto ou por um formulário criado dinamicamente, o filtro esconderá a única requisição que importa. Envie primeiro uma vez sem filtro e veja o que apareceu.

Para a requisição escolhida, anote seis coisas antes de interpretar qualquer coisa: o método, a URL completa com a string de consulta, o status, a cadeia de redirecionamentos, o Content-Type da resposta e as primeiras linhas do corpo. A referência do painel Rede do Chrome DevTools cobre as visões Headers, Response e Initiator onde isso fica, e os painéis equivalentes de outros navegadores mostram os mesmos campos.

Passo 2: confirme que é a requisição que você pensa

Uma quantidade surpreendente de erros de JSON são, na verdade, erros de roteamento. Se o manipulador do envio lançar uma exceção antes de chamar preventDefault, o navegador faz um POST nativo do formulário para o endereço do atributo action, e o servidor responde com uma página HTML completa. O manipulador nunca executa, logo nunca se pediu JSON.

Compare a URL do painel com a URL que o seu JavaScript chama. Procure um redirecionamento que termine em outro lugar, uma barra final faltando ou sobrando, uma divergência de método em que o manipulador envia GET e o endpoint aceita POST, ou um formulário que envia para um caminho relativo a partir de uma página que mudou de lugar. Siga a cadeia de redirecionamentos até o fim e anote a URL final, não a primeira.

Depois verifique o iniciador. Se a requisição veio de uma navegação de documento e não do seu script, você já encontrou o problema, e nenhuma mudança no analisador de resposta vai ajudar.

Passo 3: classifique o resultado em quatro categorias

Cada resultado pertence exatamente a uma destas, e a categoria decide onde olhar em seguida.

What you seeCategoryWhere to look
Request never finishes, TLS or connection errorNo usable responseEndpoint reachability, certificate, front-end timeout
Request completes, script gets an opaque failureAccess blockedCross-origin policy, credentials mode, missing response header
200 with an HTML body or a login formWrong representationRedirect, expired session, route fallback, error handler
Body begins with a notice then valid JSONWrong representationServer-side warning output before serialisation
Valid JSON, framework rejects itContract mismatchEnvelope shape, field names, value types

As linhas do meio são as que as pessoas diagnosticam mal, porque o status é 200. Uma página de login alcançada após um redirecionamento é uma resposta HTTP perfeitamente boa e um documento JSON muito ruim. O tipo de conteúdo application/json não conserta isso: é um rótulo, e um corpo mal rotulado continua sem poder ser analisado. Acesso bloqueado é a linha que parece uma requisição concluída: o painel mostra um status, mas a política de origem cruzada retém o corpo, então para o script que chama ela é tão inútil quanto um tempo esgotado.

A quarta linha é igualmente comum em pilhas antigas de PHP e de frameworks. Um aviso de obsolescência impresso antes da carga codificada produz um corpo que nenhum analisador JSON aceitará, e muitas vezes esse aviso é toda a explicação.

Passo 4: leia o erro do analisador ao pé da letra

O texto do erro nomeia o primeiro caractere que quebrou a análise, e isso costuma identificar a categoria sem nenhum palpite.

Error text (Chrome/V8)What it means
Unexpected token < in JSON at position 0The body starts with markup: an HTML page or an error document
Unexpected end of JSON inputEmpty body, or the response was truncated
Unexpected token o in JSON at position 1You parsed something already parsed, typically an object rather than text
Unexpected token uThe body starts with the bare word undefined, usually an empty response rendered as a string

Um corpo vazio com 200 é um caso próprio: o endpoint executou, não produziu nada e algo mais adiante tratou isso como sucesso. É uma falha de contrato da aplicação e não de transporte, e o lugar dela é o passo seguinte, não o proxy. Firefox e Safari formulam as mesmas quatro falhas de outra maneira, então compare pelo caractere ou pelo corpo vazio, e não pela string exata.

Passo 5: corrija a camada que as evidências apontam

Para um problema de roteamento, corrija a requisição: ajuste o action, impeça o manipulador de cair, alinhe o endpoint e o método, e teste de novo. Para um problema de representação, corrija o que produziu o corpo e descubra por que um documento de erro volta onde se esperavam dados. Para um problema de contrato, leia a estrutura realmente esperada pelo framework para a versão que você executa e molde a resposta conforme ela, em vez de afrouxar o cliente.

Uma resposta com acesso bloqueado precisa da sua própria passagem, porque a requisição em si pode estar correta e o corpo pode nunca chegar ao seu código. Compare o Access-Control-Allow-Origin da resposta com o Origin exato que o navegador enviou, e lembre que um curinga não satisfaz uma requisição com credenciais. Emparelhe credentials: 'include' no cliente com Access-Control-Allow-Credentials: true no servidor, e verifique se a chamada usou o modo no-cors, em que um corpo opaco é o resultado documentado e não um defeito.

Substituir o analisador por algo permissivo é a única correção que se deve evitar. Avaliar a resposta como código, ou descascar texto até que ela seja analisada, esconde a divergência de contrato e acrescenta uma superfície de injeção em troca de silêncio. Da mesma forma, verificar o status separadamente do corpo é obrigatório: um 200 que carrega um objeto de erro no nível da aplicação é normal em muitas API e deve ser tratado lendo a carga.

Quando você precisar de ajuda de outra pessoa, compartilhe um registro mínimo — método, URL, status, URL final após os redirecionamentos, tipo de conteúdo da resposta, forma do corpo e o contrato esperado — em vez de uma exportação crua. Os arquivos de rede contêm cookies, tokens, strings de consulta e os dados pessoais que estavam no formulário, e um saneador automático não é uma revisão.

  • Capture a requisição sem filtro antes de mudar qualquer código.
  • Confirme que o iniciador foi o seu script e que a URL final é o seu endpoint.
  • Classifique a resposta antes de tocar no analisador.
  • Corrija roteamento, acesso, representação ou contrato, e nunca afrouxando o analisador.

Artigos relacionados