Formularz AJAX oczekuje JSON, a otrzymuje HTML: prześledź odpowiedź krok po kroku
Odpowiedź 200 nie dowodzi, że to JSON. Przejdź panel Sieć krok po kroku i rozdziel awarie transportu, reprezentacji i kontraktu.
Formularz zostaje wysłany, pojawia się wskaźnik, a konsola mówi o nieoczekiwanym znaku < w JSON na pozycji 0. Sama strona ładuje się dobrze, dlatego pierwszym odruchem jest obwinianie parsera albo tunelu. Zwykle żaden z nich nie bierze w tym udziału: wysłanie to inne żądanie niż wczytanie strony, a wróciło coś, co nie jest JSON-em. Pracuj w kolejności, w jakiej przeglądarka naprawdę to ujawnia, bo cztery możliwe odpowiedzi wymagają czterech różnych napraw.
Krok 1: przechwyć wysłanie, które naprawdę nastąpiło
Otwórz panel Sieć przed wysłaniem, ustaw dziennik tak, aby zachowywał wpisy między nawigacjami, a następnie wykonaj jedno nieszkodliwe wysłanie testowe na koncie, którego wolno ci użyć. Wybierz żądanie rozpoczęte przez sam formularz, a nie żądanie dokumentu strony.
Uważaj na filtry. Filtr XHR lub Fetch jest wygodny, ale jeśli framework nadal wysyła przez ukryty iframe albo dynamicznie utworzony formularz, filtr ukryje jedyne ważne żądanie. Wyślij najpierw raz bez filtra i zobacz, co się pojawiło.
Dla wybranego żądania zanotuj sześć rzeczy przed jakąkolwiek interpretacją: metodę, pełny URL wraz z ciągiem zapytania, status, łańcuch przekierowań, Content-Type odpowiedzi oraz pierwsze linie treści. Dokumentacja panelu Sieć w Chrome DevTools opisuje widoki Headers, Response i Initiator, w których to jest, a odpowiedniki w innych przeglądarkach pokazują te same pola.
Krok 2: potwierdź, że to żądanie, o którym myślisz
Zaskakująco wiele błędów JSON to w rzeczywistości błędy routingu. Jeśli obsługa wysyłania zgłosi wyjątek, zanim wywoła preventDefault, przeglądarka wykona natywny POST formularza na adres z atrybutu action, a serwer odpowie pełną stroną HTML. Obsługa nigdy się nie wykona, więc JSON nigdy nie został zażądany.
Porównaj URL z panelu z URL-em, który wywołuje twój JavaScript. Szukaj przekierowania kończącego się gdzie indziej, brakującego lub zbędnego ukośnika na końcu, niezgodności metody, gdy obsługa wysyła GET, a punkt końcowy przyjmuje POST, albo formularza wysyłającego na ścieżkę względną ze strony, która się przeniosła. Śledź łańcuch przekierowań do końca i zanotuj końcowy URL, nie pierwszy.
Potem sprawdź inicjatora. Jeśli żądanie przyszło z nawigacji dokumentu, a nie ze skryptu, problem już znalazłeś i żadna zmiana parsera odpowiedzi nie pomoże.
Krok 3: zaklasyfikuj wynik do czterech kategorii
Każdy wynik należy dokładnie do jednej z nich, a kategoria decyduje, gdzie patrzeć dalej.
| 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 |
Środkowe wiersze są najczęściej mylnie diagnozowane, bo status to 200. Strona logowania osiągnięta po przekierowaniu to całkowicie dobra odpowiedź HTTP i bardzo zły dokument JSON. Typ zawartości application/json tego nie naprawia: to etykieta, a źle oznaczona treść nadal się nie parsuje. Dostęp zablokowany to wiersz, który wygląda na ukończone żądanie: panel pokazuje status, ale polityka różnych źródeł wstrzymuje treść, więc dla wywołującego skryptu jest tak bezużyteczna jak przekroczenie czasu.
Czwarty wiersz jest równie częsty na starszych stosach PHP i frameworków. Ostrzeżenie o przestarzałości wypisane przed zakodowanym ładunkiem daje treść, której nie przyjmie żaden parser JSON, a często to ostrzeżenie jest całym wyjaśnieniem.
Krok 4: czytaj błąd parsera dosłownie
Tekst błędu wskazuje pierwszy znak, który złamał parsowanie, a to zwykle identyfikuje kategorię bez żadnego zgadywania.
| 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 |
Pusta treść ze statusem 200 to osobny przypadek: punkt końcowy zadziałał, nic nie wytworzył, a coś dalej uznało to za sukces. To awaria kontraktu aplikacji, nie transportu, i jej miejsce jest w następnym kroku, nie w pośredniku. Firefox i Safari formułują te same cztery awarie inaczej, więc dopasowuj po znaku albo po pustej treści, a nie po dokładnym ciągu.
Krok 5: napraw warstwę, na którą wskazują dowody
Przy problemie routingu napraw żądanie: popraw action, nie pozwól obsłudze przelecieć, dopasuj punkt końcowy i metodę, a potem przetestuj ponownie. Przy problemie reprezentacji napraw to, co wytworzyło treść, i ustal, dlaczego dokument błędu wraca tam, gdzie oczekiwano danych. Przy problemie kontraktu przeczytaj rzeczywistą oczekiwaną strukturę frameworka dla wersji, którą uruchamiasz, i uformuj odpowiedź według niej, zamiast poluzowywać klienta.
Odpowiedź z zablokowanym dostępem wymaga własnego przebiegu, bo samo żądanie może być poprawne, a treść może nigdy nie dotrzeć do twojego kodu. Porównaj Access-Control-Allow-Origin odpowiedzi z dokładnym Origin wysłanym przez przeglądarkę i pamiętaj, że wieloznacznik nie spełnia żądania z poświadczeniami. Połącz credentials: 'include' po stronie klienta z Access-Control-Allow-Credentials: true po stronie serwera i sprawdź, czy wywołanie nie użyło trybu no-cors, w którym nieprzejrzysta treść jest udokumentowanym wynikiem, a nie usterką.
Zastąpienie parsera czymś łagodniejszym to jedyna naprawa, której należy unikać. Ocena odpowiedzi jako kodu albo zdejmowanie tekstu, aż da się sparsować, ukrywa niezgodność kontraktu i dodaje powierzchnię wstrzyknięcia w zamian za ciszę. Podobnie status trzeba sprawdzać osobno od treści: 200 z obiektem błędu na poziomie aplikacji jest normalne w wielu API i trzeba to obsłużyć, czytając ładunek.
Kiedy potrzebujesz pomocy kogoś innego, udostępnij minimalny zapis — metodę, URL, status, końcowy URL po przekierowaniach, typ zawartości odpowiedzi, kształt treści i oczekiwany kontrakt — a nie surowy eksport. Archiwa sieci zawierają ciasteczka, tokeny, ciągi zapytań i dane osobowe, które były w formularzu, a automatyczny czyściciel nie jest przeglądem.
- Przechwyć żądanie bez filtra, zanim zmienisz jakikolwiek kod.
- Potwierdź, że inicjatorem był twój skrypt, a końcowy URL to twój punkt końcowy.
- Zaklasyfikuj odpowiedź, zanim dotkniesz parsera.
- Napraw routing, dostęp, reprezentację albo kontrakt i nigdy przez poluzowanie parsera.