获取:如果状态不正常,拒绝承诺并捕获错误?

Posted

技术标签:

【中文标题】获取:如果状态不正常,拒绝承诺并捕获错误?【英文标题】:Fetch: reject promise and catch the error if status is not OK? 【发布时间】:2016-11-09 04:42:34 【问题描述】:

这就是我要做的:

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;

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

我在 Redux 中使用这个 fetch polyfill 和 redux-promise-middleware。

【问题讨论】:

您在catch 中抛出异常,但不要在catch 它。 确实到达catch(它捕获了它所连接的整个链中的所有拒绝),但catch回调不处理任何事情 -它只会重新抛出错误。将throw 替换为console.error 左右。 浏览器死机?这绝对不应该发生。 谢谢大家,我对此有点陌生,冻结是由其他原因引起的。我认为这对我来说是个问题,因为polyfill 将 404 视为成功响应。我在拒绝承诺时遇到了一些麻烦,一旦我发现它应该没问题。 更好的东西 github.com/github/fetch/issues/203#issuecomment-143347675 【参考方案1】:
function handleErrors(response) 
    if (!response.ok) 
        throw Error(response.statusText);
    
    return response;

fetch("https://example.com/api/users")
    .then(handleErrors)
    .then(response => console.log("ok") )
    .catch(error => console.log(error) );

【讨论】:

【参考方案2】:

我对任何建议的解决方案都不满意,所以我尝试了Fetch API 以找到一种方法来处理成功响应和错误响应。

计划是在这两种情况下都获得status: XXX, message: 'a message' 格式。

注意:成功响应可以包含空正文。在这种情况下,我们回退并使用Response.statusResponse.statusText 来填充结果响应对象。

fetch(url)
  .then(handleResponse)
  .then((responseJson) => 
    // Do something with the response
  )
  .catch((error) => 
    console.log(error)
  );

export const handleResponse = (res) => 
  if (!res.ok) 
    return res
      .text()
      .then(result => JSON.parse(result))
      .then(result => Promise.reject( status: result.status, message: result.message ));
  
  return res
    .json()
    .then(result => Promise.resolve(result))
    .catch(() => Promise.resolve( status: res.status, message: res.statusText ));
;

【讨论】:

【参考方案3】:

希望这对我有所帮助抛出错误不起作用

function handleErrors(response) 
  if (!response.ok) 
    return new Promise((resolve, reject) => 
      setTimeout(() => 
        reject(
          status: response.status,
          statusText: response.statusText,
        );
      , 0);
    );
  
  return response.json();


function clickHandler(event) 
  const textInput = input.value;
  let output;
  fetch(`$URL$encodeURI(textInput)`)
    .then(handleErrors)
    .then((json) => 
      output = json.contents.translated;
      console.log(output);
      outputDiv.innerhtml = "<p>" + output + "</p>";
    )
    .catch((error) => alert(error.statusText));


【讨论】:

【参考方案4】:

@fny 的答案(已接受的答案)对我不起作用。 throw new Error() 没有被 .catch 接收。我的解决方案是将fetch 包装成一个构建新承诺的函数:


function my_fetch(url, args) 
  return new Promise((resolve, reject) => 
    fetch(url, args)
    .then((response) => 
      response.text().then((body) =>  
        if (response.ok) 
          resolve(body) 
         else 
          reject(body) 
        
      )
    )
    .catch((error) =>  reject(error) )
  )

现在每个错误和不正常的返回都将被.catch 方法拾取:

my_fetch(url, args)
.then((response) => 
  // Do something with the response
)
.catch((error) => 
  // Do something with the error
)

【讨论】:

【参考方案5】:

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

    检查response.ok reject如果不OK,而不是抛出错误 进一步处理来自服务器的任何错误提示,例如验证问题
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 并降落在下一个捕获中【参考方案6】:

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

然后我会捕捉错误并检查它们是否是instanceofResponse。这样 TypeScript 就知道 error 具有 Response 属性,例如 status statusText body headers 等,我可以为每个 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`);

【讨论】:

【参考方案7】:

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

fetch Response 方便地提供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)
);

【讨论】:

我没有找到“好的”属性,而是检查了 response.status === 200。 为什么我不能从我的代码中看出为什么抛出了 TypeError?在控制台中,我看到一种情况是“net::ERR_CONNECTION_TIMED_OUT”,但在另一种情况下是“(blocked:mixed-content)”,我不想对两者都做出同样的回应。 此解决方案是否会停止在控制台中出现错误,例如 401 无效请求? 我们如何在没有网络连接或服务器响应时返回自定义响应,例如503 Service Temp. Unavailable 如果被拒绝的承诺的结果是 TypeError? 如何读取 JSON 文件?我从 BE 发送了我在 catch 块中需要的其他数据【参考方案8】:

对我来说, 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;
  


希望对您有所帮助。

【讨论】:

【参考方案9】:

我刚刚检查了响应对象的状态:

$promise.then( function successCallback(response)   
  console.log(response);
  if (response.status === 200)  ... 
);

【讨论】:

不够好,201 (Resource Created) 也是有效响应,实际上 200-299 范围内的任何内容都不是客户端错误。【参考方案10】:

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

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;

【讨论】:

你也可以从你的状态函数中拒绝承诺:function status(res) if (!res.ok) return Promise.reject(res.statusText); return res; 或者实际上你可以用你的端点给出的消息拒绝承诺。 或者实际上,如果您 jsonfy 该响应,您可以使用端点给出的消息拒绝该承诺,然后使用您从 jsonfied 响应中选择的属性返回一个 Promise denied。 .catch(error =&gt; return Promise.reject() ) 似乎毫无意义。为什么要压制有用的error 而用undefined 拒绝呢? @Vivek 或者您也可以只使用throw undefined;。我抱怨的不是拒绝,而是忽略了error。不过,可能整个事情都应该省略。 @Vivek 这可能更有意义,但这不是他们所做的。此外,使用undefined 代替带有正确消息的错误仍然是一种不好的做法。

以上是关于获取:如果状态不正常,拒绝承诺并捕获错误?的主要内容,如果未能解决你的问题,请参考以下文章

vue-resource:在拦截 ajax 错误时捕获“未捕获(在承诺中)”

Youtube API未捕获(在承诺中)错误:请求失败,状态码为403

未捕获(承诺)错误:请求失败,状态码为404

React axios api调用错误:未捕获(在承诺中)TypeError:setTempFetch不是函数

Azure AD 与 Web API 的不记名令牌身份验证无法正常工作并抛出错误,因为“此请求的授权已被拒绝”。

无法读取未定义和未处理的承诺拒绝的属性“捕获”