簡體   English   中英

獲取:拒絕 promise 並在狀態不正常時捕獲錯誤?

[英]Fetch: reject promise and catch the error if status is not OK?

這是我要做的:

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 polyfill 和redux-promise-middleware

Fetch Promise 僅在發生網絡錯誤時以 TypeError 拒絕。 由於 4xx 和 5xx 響應不是網絡錯誤,因此沒有什么可捕獲的。 您需要自己拋出錯誤才能使用Promise#catch

一個fetch Response方便地提供一個ok ,它告訴你請求是否成功。 這樣的事情應該可以解決問題:

fetch(url).then((response) => {
  if (response.ok) {
    return response.json();
  }
  throw new Error('Something went wrong');
})
.then((responseJson) => {
  // Do something with the response
})
.catch((error) => {
  console.log(error)
});

感謝大家的幫助,拒絕.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;
}

對我來說,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);
      })
    });
},

2021 年打字稿答案

我所做的是編寫一個fetch泛型的包裝器,如果response正常,它將自動ok .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 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}`);

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

現在每個錯誤和非 ok 返回都將被.catch方法拾取:

my_fetch(url, args)
.then((response) => {
  // Do something with the response
})
.catch((error) => {
  // Do something with the error
})
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) );

我剛剛檢查了響應對象的狀態:

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

我對任何建議的解決方案都不滿意,所以我使用了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 }));
};

希望這對我有幫助throw Error is not working

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

}

與大多數答案產生共鳴的另一個(較短的)版本:

fetch(url)
.then(response => response.ok ? response.json() : Promise.reject(response))
.then(json => doStuff(json)) //all good

//next line is optional
.catch(response => handleError(response)) //handle error

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM