fetch:拒绝承诺 JSON 错误对象

fetch: Reject promise with JSON error object

我有一个 HTTP API returns 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
   }
 // 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's 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;});

这是一种更简洁的方法,它依赖于 response.ok 并使用基础 JSON 数据而不是 .json() 返回的 Promise

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

上面 Jeff Posnick 的解决方案是我最喜欢的解决方案,但嵌套非常难看。

使用较新的 async/await 语法,我们可以用一种看起来更同步的方式来完成它,而没有会很快变得混乱的丑陋嵌套。

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

之所以有效,是因为 an async function always returns a promise and once we have the JSON we can then decide how to return it based on the response status (using response.ok)。

您的错误处理方式与 Jeff 的回答相同,但是您也可以使用 try/catch,error handling higher order function, or with some modification to prevent the promise rejecting you can use my favourite technique that ensures error handling is enforced as part of the developer experience

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 - Checking that the fetch was successful 为什么我们必须这样做,本质上,获取请求仅因网络错误而拒绝,获得 404 不是网络错误。

我在 MDN 找到了我的解决方案:

function fetchAndDecode(url) {
  return fetch(url).then(response => {
    if(!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    } else {
      return response.blob();
    }
  })
}

let coffee = fetchAndDecode('coffee.jpg');
let tea = fetchAndDecode('tea.jpg');

Promise.any([coffee, tea]).then(value => {
  let objectURL = URL.createObjectURL(value);
  let image = document.createElement('img');
  image.src = objectURL;
  document.body.appendChild(image);
})
.catch(e => {
  console.log(e.message);
});

也许这个选项有效

new Promise((resolve, reject) => { 
    fetch(url)
    .then(async (response) => {
        const data = await response.json();
        return { statusCode: response.status, body: data };
    })
    .then((response) => {
        if (response.statusCode >= 200 && response.statusCode < 300) {
            resolve(response.body);
        } else {
            reject(response.body);
        }
    })
});