טופס AJAX מצפה ל-JSON אך מקבל HTML: עקבו אחר התגובה שלב אחר שלב
תגובה עם סטטוס 200 אינה הוכחה ל-JSON. עברו על פאנל הרשת שלב אחר שלב והפרידו בין כשלי תעבורה, ייצוג וחוזה.
הטופס נשלח, הספינר מופיע, והקונסולה אומרת שיש תו < בלתי צפוי ב-JSON במיקום 0. הדף עצמו נטען היטב, ולכן הדחף הראשון הוא להאשים את המנתח או את המנהרה. בדרך כלל אף אחד מהם אינו מעורב: השליחה היא בקשה שונה מטעינת הדף, והיא חזרה עם משהו שאינו JSON. עבדו לפי הסדר שבו הדפדפן באמת חושף את זה, כי ארבע התשובות האפשריות דורשות ארבעה תיקונים שונים.
שלב 1: לתפוס את השליחה שבאמת קרתה
פתחו את פאנל הרשת לפני השליחה, הגדירו את היומן לשמור רשומות בין ניווטים, ואז בצעו שליחת בדיקה לא מזיקה בחשבון שמותר לכם להשתמש בו. בחרו את הבקשה שהטופס עצמו יזם, ולא את בקשת המסמך של הדף.
היזהרו ממסננים. מסנן XHR או Fetch נוח, אבל אם המסגרת עדיין שולחת דרך iframe מוסתר או טופס שנוצר דינמית, המסנן יסתיר את הבקשה היחידה שחשובה. שלחו קודם פעם אחת בלי מסנן וראו מה הופיע.
עבור הבקשה שנבחרה, רשמו שישה דברים לפני כל פרשנות: השיטה, הכתובת המלאה כולל מחרוזת השאילתה, הסטטוס, שרשרת ההפניה מחדש, ה-Content-Type של התגובה, והשורות הראשונות של הגוף. המדריך לפאנל הרשת של Chrome DevTools מכסה את תצוגות Headers, Response ו-Initiator שבהןהדברים האלה נמצאים, והפאנלים המקבילים בדפדפנים אחרים מציגים את אותם שדות.
שלב 2: לוודא שזו הבקשה שאתם חושבים
מספר מפתיע של שגיאות JSON הוא למעשה שגיאות ניתוב. אם מטפל השליחה זורק חריגה לפני שהוא קורא ל-preventDefault, הדפדפן מבצע POST מקורי של הטופס לכתובת שכתובה במאפיין action, והשרת עונה בעמוד HTML מלא. המטפל לעולם אינו רץ, ולכן JSON מעולם לא התבקש.
השוו את הכתובת בפאנל לכתובת שה-JavaScript שלכם קורא לה. חפשו הפניה מחדש שמסתיימת במקום אחר, לוכסן סיום חסר או עודף, אי-התאמה בשיטה שבה המטפל שולח GET והנקודה הקצה מקבלת POST, או טופס ששולח לנתיב יחסי מדף שהועבר. עקבו אחר שרשרת ההפניות עד הסוף ורשמו את הכתובת הסופית, לא את הראשונה.
לאחר מכן בדקו את היוזם. אם הבקשה הגיעה מניווט מסמך ולא מהסקריפט שלכם, כבר מצאתם את הבעיה, ושום שינוי במנתח התגובה לא יעזור.
שלב 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 הסופי הוא הנקודה הקצה שלכם.
- סווגו את התגובה לפני שנוגעים במנתח.
- תקנו ניתוב, גישה, ייצוג או חוזה, ואף פעם לא בהרפיית המנתח.