Un formulario AJAX espera JSON pero recibe HTML: revisa a resposta paso a paso
Unha resposta 200 non demostra que sexa JSON. Percorre o panel Rede paso a paso e separa os fallos de transporte, de representación e de contrato.
O formulario envíase, aparece o indicador e a consola di que hai un símbolo < inesperado en JSON na posición 0. A páxina en si carga ben, e por iso o primeiro impulso é culpar o analizador ou o túnel. Normalmente non intervén ningún dos dous: o envío é unha petición distinta da carga da páxina, e volveu con algo que non é JSON. Traballa na orde en que o navegador o revela de verdade, porque as catro respostas posibles necesitan catro correccións distintas.
Paso 1: captura o envío que ocorreu realmente
Abre o panel Rede antes de enviar, deixa o rexistro configurado para conservar as entradas entre navegacións e fai un envío de proba inofensivo cunha conta que teñas permiso para usar. Selecciona a petición que iniciou o propio formulario, non a petición do documento da páxina.
Ten coidado cos filtros. Un filtro XHR ou Fetch é cómodo, pero se o framework aínda envía mediante un iframe oculto ou un formulario creado dinamicamente, o filtro ocultará a única petición que importa. Envía primeiro unha vez sen filtro e mira que apareceu.
Para a petición elixida, anota seis cousas antes de interpretar nada: o método, a URL completa coa cadea de consulta, o estado, a cadea de redireccións, o Content-Type da resposta e as primeiras liñas do corpo. A referencia do panel Rede de Chrome DevTools cobre as vistas Headers, Response e Initiator onde están eses datos, e os paneis equivalentes doutros navegadores mostran os mesmos campos.
Paso 2: confirma que é a petición que cres
Unha cantidade sorprendente de erros de JSON son en realidade erros de encamiñamento. Se o manexador do envío lanza unha excepción antes de chamar a preventDefault, o navegador fai un POST nativo do formulario á dirección do atributo action, e o servidor responde cunha páxina HTML completa. O manexador nunca se executa, así que nunca se pediu JSON.
Compara a URL do panel coa URL que chama o teu JavaScript. Busca unha redirección que remate noutro sitio, unha barra final que falte ou que sobre, un desaxuste de método no que o manexador envía GET e o extremo acepta POST, ou un formulario que envía a unha ruta relativa desde unha páxina que se moveu. Segue a cadea de redireccións ata o final e anota a URL final, non a primeira.
Despois revisa o iniciador. Se a petición veu dunha navegación de documento e non do teu script, xa atopaches o problema, e ningún cambio nun analizador de resposta vai axudar.
Paso 3: clasifica o resultado en catro categorías
Cada resultado pertence exactamente a unha destas, e a categoría decide onde mirar despois.
| What you see | Category | Where to look |
|---|---|---|
| Request never finishes, TLS or connection error | No usable response | Endpoint reachability, certificate, front-end timeout |
| Request completes, script gets an opaque failure | Access blocked | Cross-origin policy, credentials mode, missing response header |
| 200 with an HTML body or a login form | Wrong representation | Redirect, expired session, route fallback, error handler |
| Body begins with a notice then valid JSON | Wrong representation | Server-side warning output before serialisation |
| Valid JSON, framework rejects it | Contract mismatch | Envelope shape, field names, value types |
As filas centrais son as que a xente diagnostica mal, porque o estado é 200. Unha páxina de inicio de sesión á que se chega tras unha redirección é unha resposta HTTP perfectamente boa e un documento JSON moi malo. O tipo de contido application/json non o arranxa: é unha etiqueta, e un corpo mal etiquetado segue sen poder analizarse. Acceso bloqueado é a fila que parece unha petición completada: o panel mostra un estado, pero a política de orixe cruzada retén o corpo, así que para o script que chama é tan inútil como un tempo de espera esgotado.
A cuarta fila é igual de común en pilas vellas de PHP e de frameworks. Un aviso de obsolescencia impreso antes da carga codificada produce un corpo que ningún analizador JSON aceptará, e a miúdo ese aviso é toda a explicación.
Paso 4: le o erro do analizador ao pé da letra
O texto do erro nomea o primeiro carácter que rompeu a análise, o que adoita identificar a categoría sen ningunha conxectura.
| Error text (Chrome/V8) | What it means |
|---|---|
| Unexpected token < in JSON at position 0 | The body starts with markup: an HTML page or an error document |
| Unexpected end of JSON input | Empty body, or the response was truncated |
| Unexpected token o in JSON at position 1 | You parsed something already parsed, typically an object rather than text |
| Unexpected token u | The body starts with the bare word undefined, usually an empty response rendered as a string |
Un corpo baleiro cun 200 é un caso propio: o extremo executouse, non produciu nada e algo máis abaixo tomouno como un éxito. É un fallo de contrato da aplicación e non de transporte, e o seu sitio está no paso seguinte, non no proxy. Firefox e Safari formulan os mesmos catro fallos doutro xeito, así que compara polo carácter ou polo corpo baleiro e non pola cadea exacta.
Paso 5: corrixe a capa que sinalan as probas
Para un problema de encamiñamento, corrixe a petición: arranxa o action, evita que o manexador caia, fai que coincidan o extremo e o método, e volve probar. Para un problema de representación, corrixe o que produciu o corpo e averigua por que se devolve un documento de erro onde se esperaban datos. Para un problema de contrato, le a estrutura real que o framework espera para a versión que executas e dálle forma á resposta conforme a ela en lugar de afrouxar o cliente.
Unha resposta con acceso bloqueado necesita a súa propia pasada, porque a petición pode ser correcta e o corpo pode non chegar nunca ao teu código. Compara o Access-Control-Allow-Origin da resposta co Origin exacto que enviou o navegador, e lembra que un comodín non satisface unha petición con credenciais. Emparella credentials: 'include' no cliente con Access-Control-Allow-Credentials: true no servidor, e comproba se a chamada usou o modo no-cors, onde un corpo opaco é o resultado documentado e non un defecto.
Substituír o analizador por algo permisivo é a única corrección que hai que evitar. Avaliar a resposta como código, ou retirar texto ata que se analice, oculta o desaxuste de contrato e engade unha superficie de inxección a cambio de silencio. Igualmente, comprobar o estado por separado do corpo é obrigatorio: un 200 que leva un obxecto de erro de nivel de aplicación é normal en moitas API e debe tratarse lendo a carga.
Cando necesites axuda doutra persoa, comparte un rexistro mínimo — método, URL, estado, URL final tras as redireccións, tipo de contido da resposta, forma do corpo e o contrato esperado — en lugar dunha exportación en bruto. Os arquivos de rede conteñen cookies, tokens, cadeas de consulta e os datos persoais que había no formulario, e un saneador automático non é unha revisión.
- Captura a petición sen filtro antes de cambiar calquera código.
- Confirma que o iniciador foi o teu script e que a URL final é o teu extremo.
- Clasifica a resposta antes de tocar o analizador.
- Corrixe encamiñamento, acceso, representación ou contrato, e nunca afrouxando o analizador.