web-dev-qa-db-de.com

fetch: Versprechen mit JSON-Fehlerobjekt ablehnen

Ich habe eine HTTP-API, die JSON-Daten sowohl bei Erfolg als auch bei Misserfolg zurückgibt.

Ein Beispielfehler würde folgendermaßen aussehen:

~ ◆ http get http://localhost:5000/api/isbn/2266202022 
HTTP/1.1 400 BAD REQUEST
Content-Length: 171
Content-Type: application/json
Server: TornadoServer/4.0

{
    "message": "There was an issue with at least some of the supplied values.", 
    "payload": {
        "isbn": "Could not find match for ISBN."
    }, 
    "type": "validation"
}

Mit meinem JavaScript-Code möchte ich Folgendes erreichen:

fetch(url)
  .then((resp) => {
     if (resp.status >= 200 && resp.status < 300) {
       return resp.json();
     } else {
       // This does not work, since the Promise returned by `json()` is never fulfilled
       return Promise.reject(resp.json());
     }
   })
   .catch((error) => {
     // Do something with the error object
   }
54
jbaiter
 // This does not work, since the Promise returned by `json()` is never fulfilled
return Promise.reject(resp.json());

Nun die resp.json Versprechen wird erfüllt, nur Promise.reject wartet nicht darauf und lehnt mit einem Versprechen sofort ab.

Ich gehe davon aus, dass Sie lieber Folgendes tun möchten:

fetch(url).then((resp) => {
  let json = resp.json(); // there's always a body
  if (resp.status >= 200 && resp.status < 300) {
    return json;
  } else {
    return json.then(Promise.reject.bind(Promise));
  }
})

(oder explizit geschrieben)

    return json.then(err => {throw err;});
86
Bergi

Hier ist ein etwas übersichtlicherer Ansatz, der sich auf response.ok stützt und die zugrunde liegenden JSON-Daten anstelle des von .json() zurückgegebenen Promise verwendet.

function myFetchWrapper(url) {
  return fetch(url).then(response => {
    return response.json().then(json => {
      return response.ok ? json : Promise.reject(json);
    });
  });
}

// This should trigger the .then() with the JSON response,
// since the response is an HTTP 200.
myFetchWrapper('http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY').then(console.log.bind(console));

// This should trigger the .catch() with the JSON response,
// since the response is an HTTP 400.
myFetchWrapper('https://content.googleapis.com/youtube/v3/search').catch(console.warn.bind(console));
37
Jeff Posnick

Die obige Lösung von Jeff Posnick ist meine Lieblingsmethode, aber die Verschachtelung ist ziemlich hässlich.

Mit der neueren async/await -Syntax können wir dies auf synchronere Weise tun, ohne dass das hässliche Verschachteln, das schnell verwirrend werden kann.

async function myFetchWrapper(url) {
  const response = await fetch(url);
  const json = await response.json();
  return response.ok ? json : Promise.reject(json);
}

Dies funktioniert, weil eine asynchrone Funktion gibt immer ein Versprechen zurück und sobald wir den JSON haben, können wir anhand des Antwortstatus entscheiden, wie wir ihn zurückgeben möchten (mit response.ok ). .

Sie würden Fehler genauso behandeln wie in Jeffs Antwort, oder Sie könnten try/catch oder sogar ein Fehlerbehandlung höherer Ordnung verwenden.

const url = 'http://api.openweathermap.org/data/2.5/weather?q=Brooklyn,NY'

// Example with Promises
myFetchWrapper(url)
  .then((res) => ...)
  .catch((err) => ...);

// Example with try/catch (presuming wrapped in an async function)
try {
  const data = await myFetchWrapper(url);
  ...
} catch (err) {
  throw new Error(err.message);
}

Lesen Sie auch MDN - Überprüfen, ob der Abruf erfolgreich war Aus diesem Grund wird eine Abrufanforderung im Wesentlichen nur mit Netzwerkfehlern zurückgewiesen. Das Abrufen eines 404 ist kein Netzwerkfehler.

5
tomhughes