AJAX формуляр очаква JSON, но получава HTML: проследете отговора стъпка по стъпка
Статус 200 не е доказателство за JSON. Проследете панела Мрежа стъпка по стъпка и разделете отказите на транспорта, на представянето и на договора.
Формулярът се изпраща, появи се индикаторът и конзолата казва неочакван символ < в JSON на позиция 0. Самата страница се зарежда нормално и затова първият рефлекс е да обвиним парсера или тунела. Обикновено нито един от двата не участва: изпращането е отделна заявка от зареждането на страницата и се е върнало с нещо, което не е JSON. Работете в реда, в който браузърът действително го разкрива, защото четирите възможни отговора изискват четири различни поправки.
Стъпка 1: уловете изпращането, което наистина се е случило
Отворете панела Мрежа преди да изпратите, задайте журнала да запазва записите при навигация и след това направете едно безобидно тестово изпращане с профил, който ви е разрешено да използвате. Изберете заявката, която самият формуляр е стартирал, а не заявката за документа на страницата.
Внимавайте с филтрите. Филтърът XHR или Fetch е удобен, но ако фреймуъркът още изпраща през скрит iframe или динамично създаден формуляр, филтърът ще скрие единствената важна заявка. Изпратете първо веднъж без филтър и вижте какво се появи.
За избраната заявка запишете шест неща, преди да тълкувате каквото и да е: метод, пълен URL с низа на заявката, статус, веригата от пренасочвания, Content-Type на отговора и първите редове на тялото. Справочникът за панела Мрежа на Chrome DevTools описва изгледите Headers, Response и Initiator, където се намира това, а аналогичните панели в други браузъри показват същите полета.
Стъпка 2: потвърдете, че това е заявката, която мислите
Изненадващо голяма част от грешките с JSON всъщност са грешки в маршрутизирането. Ако обработчикът на изпращането хвърли изключение, преди да извика preventDefault, браузърът извършва присъщ POST на формуляра към адреса от атрибута action и сървърът отговаря с цяла HTML страница. Обработчикът никога не се изпълнява, тоест JSON никога не е бил поискан.
Сравнете URL в панела с URL, който вика вашият JavaScript. Търсете пренасочване, което завършва другаде, липсваща или излишна наклонена черта в края, несъответствие в метода, при което обработчикът праща GET, а крайната точка приема POST, или формуляр, който изпраща към относителен път от страница, която се е преместила. Проследете веригата от пренасочвания до края и отбележете крайния URL, не първия.
След това проверете инициатора. Ако заявката е дошла от навигация на документ, а не от вашия скрипт, вече сте намерили проблема и никаква промяна в парсера на отговора няма да помогне.
Стъпка 3: разпределете резултата в четири категории
Всеки резултат принадлежи точно на една от тях и категорията решава къде да гледате след това.
| 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 |
Средните редове са тези, които хората диагностицират погрешно, защото статусът е 200. Страница за вход, достигната след пренасочване, е напълно добър HTTP отговор и много лош JSON документ. Типът на съдържанието application/json не поправя това: той е етикет и неправилно етикетирано тяло пак не се парсва. Достъпът е блокиран е редът, който изглежда завършена заявка: панелът показва статус, но политиката за различни източници задържа тялото, така че за викащия скрипт то е толкова неизползваемо, колкото и изтичане на времето.
Четвъртият ред е също толкова чест при по-стари стекове с PHP и фреймуърци. Предупреждение за отпадане, отпечатано преди кодирания товар, създава тяло, което никой JSON парсер няма да приеме, и предупреждението често е цялото обяснение.
Стъпка 4: четете грешката на парсера буквално
Текстът на грешката назовава първия символ, който е счупил разбора, и това обикновено определя категорията без никакво гадаене.
| 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 |
Празното тяло със статус 200 е отделен случай: крайната точка е работила, не е произвела нищо и нещо по-надолу го е приело за успех. Това е отказ на договора на приложението, а не на транспорта, и мястото му е в следващата стъпка, не в проксито. Firefox и Safari формулират същите четири отказа по друг начин, затова сравнявайте по символа или по празното тяло, а не по точния низ.
Стъпка 5: поправете слоя, който доказателствата сочат
При проблем с маршрутизирането поправете заявката: коригирайте action, не позволявайте на обработчика да пропадне, приведете крайната точка и метода в съответствие и тествайте отново. При проблем с представянето поправете това, което е произвело тялото, и разберете защо се връща документ за грешка там, където се очакват данни. При проблем с договора прочетете действителната очаквана структура на фреймуърка за версията, която изпълнявате, и оформете отговора по нея, вместо да разхлабвате клиента.
Отговорът с блокиран достъп изисква отделно минаване, защото самата заявка може да е правилна, а тялото може никога да не стигне до кода ви. Сравнете Access-Control-Allow-Origin на отговора с точния Origin, който браузърът е изпратил, и помнете, че заместващият знак не удовлетворява заявка с идентификационни данни. Свържете credentials: 'include' от страна на клиента с Access-Control-Allow-Credentials: true от страна на сървъра и проверете дали извикването не е използвало режим no-cors, където непрозрачното тяло е документиран резултат, а не дефект.
Замяната на парсера с нещо по-снизходително е единствената поправка, която трябва да се избягва. Изпълняването на отговора като код или отстраняването на текст, докато се парсне, скрива несъответствието в договора и добавя повърхност за инжектиране в замяна на тишина. Също така проверката на статуса отделно от тялото е задължителна: статус 200 с обект за грешка на ниво приложение е нормален в много API и трябва да се обработва чрез четене на товара.
Когато ви трябва помощ от някого, споделете минимален запис — метод, URL, статус, краен URL след пренасочванията, тип на съдържанието на отговора, форма на тялото и очаквания договор — вместо суров експорт. Архивите на мрежата съдържат бисквитки, токени, низове на заявката и личните данни, които са били във формуляра, а автоматичният изчистващ инструмент не е преглед.
- Уловете заявката без филтър, преди да промените какъвто и да е код.
- Потвърдете, че инициаторът е бил вашият скрипт, а крайният URL е вашата крайна точка.
- Класифицирайте отговора, преди да пипате парсера.
- Поправете маршрутизирането, достъпа, представянето или договора и никога чрез разхлабване на парсера.