web-dev-qa-db-fra.com

fetch: rejeter la promesse avec un objet d'erreur JSON

J'ai une API HTTP qui renvoie les données JSON en cas de succès ou d'échec.

Un exemple d'échec ressemblerait à ceci:

~ ◆ 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"
}

Ce que je veux réaliser dans mon code JavaScript ressemble à ceci:

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());

Eh bien, le resp.json promesse sera être remplie, seulement Promise.reject n'attend pas et rejette immédiatement avec une promesse.

Je suppose que vous voulez plutôt faire ce qui suit:

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));
  }
})

(ou, écrit explicitement)

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

Voici une approche un peu plus propre qui s'appuie sur response.ok et utilise les données JSON sous-jacentes à la place du Promise renvoyé par .json().

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

La solution ci-dessus de Jeff Posnick est ma façon préférée de le faire, mais la nidification est assez moche.

Avec la nouvelle syntaxe async/wait /wait , nous pouvons le faire de manière plus synchrone, sans imbrication imbécile qui peut rapidement prêter à confusion.

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

Cela fonctionne car, ne fonction asynchrone retourne toujours une promesse et une fois que nous avons le JSON, nous pouvons ensuite décider comment le renvoyer en fonction du statut de la réponse (en utilisant response.ok ) .

Vous voudriez gérer les erreurs de la même manière que dans la réponse de Jeff ou utiliser try/catch, ou même un gestion des erreurs, fonction d'ordre supérieur .

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);
}

Il est également intéressant de lire MDN - Vérifier que l’extraction a abouti pour cette raison, une demande d’extraction refuse uniquement les erreurs réseau, obtenir un numéro 404 n’est pas une erreur réseau.

5
tomhughes