Φόρμα 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 μετά τις ανακατευθύνσεις, τύπο περιεχομένου της απόκρισης, μορφή σώματος και την αναμενόμενη σύμβαση — αντί για ακατέργαστη εξαγωγή. Τα αρχεία δικτύου περιέχουν cookies, tokens, ερωτήματα και τα προσωπικά δεδομένα που ήταν στη φόρμα, και ένας αυτόματος καθαριστής δεν είναι έλεγχος.
- Καταγράψτε το αίτημα χωρίς φίλτρο πριν αλλάξετε οποιονδήποτε κώδικα.
- Επιβεβαιώστε ότι εκκινητής ήταν το σενάριό σας και τελικό URL το τελικό σας σημείο.
- Ταξινομήστε την απόκριση πριν αγγίξετε τον αναλυτή.
- Διορθώστε δρομολόγηση, πρόσβαση, αναπαράσταση ή σύμβαση, και ποτέ χαλαρώνοντας τον αναλυτή.