Подтвердить что ты не робот

Fetch: Отклонить обещание с помощью объекта ошибки JSON

У меня есть HTTP API, который возвращает данные JSON как по успеху, так и по ошибке.

Пример неудачи будет выглядеть так:

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

То, что я хочу достичь в моем JavaScript-коде, выглядит примерно так:

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
   }
4b9b3361

Ответ 1

 // This does not work, since the Promise returned by `json()` is never fulfilled
return Promise.reject(resp.json());

Ну, обещание resp.json будет выполнено, только Promise.reject не ждет его и сразу же откажется с обещанием.

Я предполагаю, что вы скорее захотите сделать следующее:

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

(или, явно написано)

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

Ответ 2

Здесь несколько более чистый подход, который опирается на response.ok и использует базовые данные JSON вместо Promise, возвращаемого .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));

Ответ 3

Приведенное выше решение от Джеффа Посника - мой любимый способ сделать это, но вложение довольно уродливо.

С новым синтаксисом async/await мы можем сделать это более синхронно, без отвратительного вложения, которое может быстро запутать.

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

Это работает, потому что асинхронная функция всегда возвращает обещание, и как только у нас есть JSON, мы можем затем решить, как его вернуть, основываясь на статусе ответа (используя response.ok).

Вы будете обрабатывать ошибки так же, как в ответе Джеффа, или вы можете использовать try/catch, или даже функцию обработки ошибок более высокого порядка.

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

Также стоит прочитать MDN - Проверка того, что выборка прошла успешно, по той причине, что мы должны это сделать, по сути, запрос на выборку отклоняется только с ошибками сети, получение 404 не является сетевой ошибкой.