fetch:使用JSON错误对象拒绝承诺

时间:2022-08-23 11:38:06

I have an HTTP API that returns JSON data both on success and on failure.

我有一个HTTP API,它在成功和失败时返回JSON数据。

An example failure would look like this:

示例失败将如下所示:

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

What I want to achieve in my JavaScript code is something like this:

我想在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
   }

3 个解决方案

#1


78  

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

Well, the resp.json promise will be fulfilled, only Promise.reject doesn't wait for it and immediately rejects with a promise.

好吧,resp.json承诺将被实现,只有Promise.reject不会等待它并立即拒绝承诺。

I'll assume that you rather want to do the following:

我假设你宁愿做以下事情:

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

(or, written explicitly)

(或明确写)

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

#2


28  

Here's a somewhat cleaner approach that relies on response.ok and makes use of the underlying JSON data instead of the Promise returned by .json().

这是一种更简洁的方法,依赖于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=*lyn,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


0  

The solution above from Jeff Posnick is my favourite way of doing it, but the nesting is pretty ugly.

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

With the newer async/await syntax we can do it in a more synchronous looking way, without the ugly nesting that can quickly become confusing.

使用更新的async / await语法,我们可以以更加同步的方式执行此操作,而不会使丑陋的嵌套很快变得混乱。

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

This works because, 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).

这是有效的,因为异步函数总是返回一个promise,一旦我们有了JSON,我们就可以决定如何根据响应状态返回它(使用response.ok)。

You would error handle the same way as you would in Jeff's answer, or you could use try/catch, or even an error handling higher order function.

您将使用与Jeff的答案相同的方式处理错误,或者您可以使用try / catch,甚至是处理更高阶函数的错误。

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

myFetchWrapper(url)
  .then((res) => ...)
  .catch((err) => ...);

#1


78  

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

Well, the resp.json promise will be fulfilled, only Promise.reject doesn't wait for it and immediately rejects with a promise.

好吧,resp.json承诺将被实现,只有Promise.reject不会等待它并立即拒绝承诺。

I'll assume that you rather want to do the following:

我假设你宁愿做以下事情:

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

(or, written explicitly)

(或明确写)

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

#2


28  

Here's a somewhat cleaner approach that relies on response.ok and makes use of the underlying JSON data instead of the Promise returned by .json().

这是一种更简洁的方法,依赖于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=*lyn,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


0  

The solution above from Jeff Posnick is my favourite way of doing it, but the nesting is pretty ugly.

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

With the newer async/await syntax we can do it in a more synchronous looking way, without the ugly nesting that can quickly become confusing.

使用更新的async / await语法,我们可以以更加同步的方式执行此操作,而不会使丑陋的嵌套很快变得混乱。

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

This works because, 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).

这是有效的,因为异步函数总是返回一个promise,一旦我们有了JSON,我们就可以决定如何根据响应状态返回它(使用response.ok)。

You would error handle the same way as you would in Jeff's answer, or you could use try/catch, or even an error handling higher order function.

您将使用与Jeff的答案相同的方式处理错误,或者您可以使用try / catch,甚至是处理更高阶函数的错误。

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

myFetchWrapper(url)
  .then((res) => ...)
  .catch((err) => ...);