获取:拒绝Promise并在状态不正常时捕获错误?

IT技术 javascript redux fetch-api
2021-01-23 00:14:30

这是我要做的:

import 'whatwg-fetch';

function fetchVehicle(id) {
    return dispatch => {
        return dispatch({
            type: 'FETCH_VEHICLE',
            payload: fetch(`http://swapi.co/api/vehicles/${id}/`)
                .then(status)
                .then(res => res.json())            
                .catch(error => {
                    throw(error);
                })
            });
    };
}

function status(res) {
    if (!res.ok) {
        return Promise.reject()
    }
    return res;
}

编辑:Promise不会被拒绝,这就是我想要弄清楚的。

在 Redux 中使用这个fetch polyfillredux-promise-middleware

6个回答

当发生网络错误时,FetchPromise仅会以 TypeError 拒绝。由于 4xx 和 5xx 响应不是网络错误,因此没有什么可捕获的。您需要自己抛出错误才能使用Promise#catch.

一个获取响应方便地提供的ok ,它告诉你的请求是否成功。像这样的事情应该可以解决问题:

fetch(url).then((response) => {
  if (response.ok) {
    return response.json();
  } else {
    throw new Error('Something went wrong');
  }
})
.then((responseJson) => {
  // Do something with the response
})
.catch((error) => {
  console.log(error)
});
此解决方案会停止在控制台中收到错误,例如 401 无效请求吗?
2021-03-14 00:14:30
我没有找到属性“ok”,而是检查了 response.status === 200。
2021-03-15 00:14:30
为什么我不能从我的代码中看出 TypeError 被抛出的原因?在控制台中,我看到在一种情况下它是“net::ERR_CONNECTION_TIMED_OUT”,但在另一种情况下它是“(blocked:mixed-content)”,我不想对两者做出相同的回应。
2021-03-29 00:14:30
如何在 catch 中读取 JSON?我从 BE 发送我在 catch 块中需要的附加数据
2021-04-06 00:14:30
当没有网络连接或当服务器响应例如 a503 Service Temp. Unavailable如果被拒绝的Promise的结果是 a时,我们如何返回自定义响应TypeError
2021-04-07 00:14:30

感谢大家的帮助,拒绝Promise.catch()解决了我的问题:

export function fetchVehicle(id) {
    return dispatch => {
        return dispatch({
            type: 'FETCH_VEHICLE',
            payload: fetch(`http://swapi.co/api/vehicles/${id}/`)
                .then(status)
                .then(res => res.json())    
                .catch(error => {
                    return Promise.reject()
                })
            });
    };
}


function status(res) {
    if (!res.ok) {
        throw new Error(res.statusText);
    }
    return res;
}
或者实际上,如果您对该响应进行 jsonfy,然后使用您从 jsonfied 响应中选择的属性返回一个被拒绝的 Promise,则您可以使用端点给出的消息拒绝该Promise。
2021-03-18 00:14:30
@Vivek 这可能更有意义,但这不是他们所做的。使用undefined正确的消息代替错误仍然是一种不好的做法。
2021-03-19 00:14:30
@Vivek 或者你也可以同样只是throw undefined;. 我抱怨的不是拒绝,而是忽略了error. 可能整个事情都应该被省略。
2021-03-22 00:14:30
.catch(error => { return Promise.reject() })似乎很没有意义。为什么要抑制有用的error而拒绝用undefined呢?
2021-03-27 00:14:30
您也可以像这样拒绝状态函数中的 Promise:function status(res) { if (!res.ok) { return Promise.reject(res.statusText); } return res; } 或者实际上您可以使用端点提供的消息拒绝 Promise
2021-04-03 00:14:30

对我来说,fny 的答案真的解决了一切。由于 fetch 不会抛出错误,我们需要自己抛出/处理错误。使用 async/await 发布我的解决方案。我认为它更具有前瞻性和可读性

解决方案一:不抛出错误,自己处理错误

  async _fetch(request) {
    const fetchResult = await fetch(request); //Making the req
    const result = await fetchResult.json(); // parsing the response

    if (fetchResult.ok) {
      return result; // return success object
    }


    const responseError = {
      type: 'Error',
      message: result.message || 'Something went wrong',
      data: result.data || '',
      code: result.code || '',
    };

    const error = new Error();
    error.info = responseError;

    return (error);
  }

这里如果我们得到一个错误,我们正在构建一个错误对象,一个普通的 JS 对象并返回它,缺点是我们需要在外面处理它。如何使用:

  const userSaved = await apiCall(data); // calling fetch
  if (userSaved instanceof Error) {
    debug.log('Failed saving user', userSaved); // handle error

    return;
  }
  debug.log('Success saving user', userSaved); // handle success

解决方案2:抛出错误,使用try/catch

async _fetch(request) {
    const fetchResult = await fetch(request);
    const result = await fetchResult.json();

    if (fetchResult.ok) {
      return result;
    }

    const responseError = {
      type: 'Error',
      message: result.message || 'Something went wrong',
      data: result.data || '',
      code: result.code || '',
    };

    let error = new Error();
    error = { ...error, ...responseError };
    throw (error);
  }

这里我们抛出我们创建的错误,因为 Error ctor 只批准字符串,我创建了普通的 Error js 对象,用途是:

  try {
    const userSaved = await apiCall(data); // calling fetch
    debug.log('Success saving user', userSaved); // handle success
  } catch (e) {
    debug.log('Failed saving user', userSaved); // handle error
  }

解决方案 3:使用客户错误

  async _fetch(request) {
    const fetchResult = await fetch(request);
    const result = await fetchResult.json();

    if (fetchResult.ok) {
      return result;
    }

    throw new ClassError(result.message, result.data, result.code);
  }

和:

class ClassError extends Error {

  constructor(message = 'Something went wrong', data = '', code = '') {
    super();
    this.message = message;
    this.data = data;
    this.code = code;
  }

}

希望它有所帮助。

以下login with username and password示例显示了如何:

  1. 查看 response.ok
  2. reject 如果不正常,而不是抛出错误
  3. 进一步处理来自服务器的任何错误提示,例如验证问题
login() {
  const url = "https://example.com/api/users/login";
  const headers = {
    Accept: "application/json",
    "Content-Type": "application/json",
  };
  fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify({
      email: this.username,
      password: this.password,
    }),
  })
    .then((response) => {
      // 1. check response.ok
      if (response.ok) {
        return response.json();
      }
      return Promise.reject(response); // 2. reject instead of throw
    })
    .then((json) => {
      // all good, token is ready
      this.store.commit("token", json.access_token);
    })
    .catch((response) => {
      console.log(response.status, response.statusText);
      // 3. get error messages, if any
      response.json().then((json: any) => {
        console.log(json);
      })
    });
},

这对我有用!似乎 throw 不像在其他语言中习惯的那样工作。简单地返回 Promise.reject() 将传递所有后续的 .thens 并进入下一个捕获
2021-04-08 00:14:30

2021 年typescript答案

我所做的是编写一个fetch带有泛型包装器,如果responseok,它将自动.json()并键入断言结果,否则包装器会抛出response

export const fetcher = async <T>(input: RequestInfo, init?: RequestInit) => {
  const response = await fetch(input, init);

  if (!response.ok) {
    throw response;
  }

  return response.json() as Promise<T>;
};

然后我会发现错误并检查它们是否是instanceof Response. 这样 TypeScript 就知道它error具有Response诸如status statusText body headersetc. 之类的属性,我可以为每个4xx 5xx状态代码应用自定义消息

try {
  return await fetcher<LoginResponse>("http://localhost:8080/login", {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email: "user@example.com", password: "passw0rd" }),
  });
} catch (error) {
  if (error instanceof Response) {
    switch (error.status) {
      case 401:
        throw new Error("Invalid login credentials");
      /* ... */
      default:
        throw new Error(`Unknown server error occured: ${error.statusText}`);
    }
  }
  throw new Error(`Something went wrong: ${error.message || error}`);
}

如果发生网络错误之类的事情,可以在instanceof Response检查之外使用更通用的消息来捕获它,

throw new Error(`Something went wrong: ${error.message || error}`);