【问题标题】:How to handle errors in fetch() responses with Redux-Saga?如何使用 Redux-Saga 处理 fetch() 响应中的错误?
【发布时间】:2017-02-21 19:21:16
【问题描述】:

我尝试使用 redux-saga 处理来自服务器的 Unauthorized 错误。这是我的传奇:

function* logIn(action) {
  try {
    const user = yield call(Api.logIn, action);
    yield put({type: types.LOG_IN_SUCCEEDED, user});
  } catch (error) {
    yield put({type: types.LOG_IN_FAILED, error});
  }
}

我这样获取数据:

fetchUser(action) {
  const {username, password} = action.user;
  const body = {username, password};
  return fetch(LOGIN_URL, {
    method,
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body)
  })
    .then(res => {
      res.json().then(json => {
        if (res.status >= 200 && res.status < 300) {
          return json
        } else {
          throw res
        }
      })
    })
    .catch(error => {throw error});
}

但无论如何,当我期望{type: 'LOG_IN_FAILED', error: 'Unauthorized'} 时,结果是{type: 'LOG_IN_SUCCEEDED', user: undefined}。我的错误在哪里?如何正确使用 Redux-Saga 处理错误?

【问题讨论】:

    标签: javascript redux fetch redux-saga


    【解决方案1】:

    不要在你的fetchUser 方法和你的传奇中处理thenerror。既然你已经在你的传奇中try/catching,你可以在那里处理它。

    示例

    传奇

    function* logIn(action) {
      try {
        const response = yield call(Api.logIn, action);
    
        if (response.status >= 200 && response.status < 300) {
          const user = yield response.json();
    
          yield put({ type: types.LOG_IN_SUCCEEDED, user });
        } else {
          throw response;
        }
      } catch (error) {
        yield put({ type: types.LOG_IN_FAILED, error });
      }
    }
    

    获取

    fetchUser(action) {
      const { username, password } = action.user;
      const body = { username, password };
    
      return fetch(LOGIN_URL, {
        method,
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(body)
      })
    }
    

    附带说明:我发现 fetch 的 api 有点尴尬,因为它会在您发出请求时返回 then-able 响应。那里有很多图书馆。我个人更喜欢axios,它默认返回json。

    【讨论】:

    • 非常感谢!
    【解决方案2】:

    如果您想使用 if 语句验证响应状态 if(res.status &gt;= 200 &amp;&amp; res.status &lt; 300) {,您需要将它放在定义 res 的第一个承诺中,它当前位于 res.json() 的已解决承诺中

    .then(res => {
       if (res.status >= 200 && res.status < 300) {
          res.json().then(json => {
             return json
        }
      })
    })
    

    【讨论】:

      【解决方案3】:

      如果您需要在一个 saga 中进行多个 API 调用,更好的方法是在 fetch 阶段抛出错误:

      获取

      export const getCounterTypes = (user) => {
        const url = API_URL + `api/v4/counters/counter_types`;
      
        const headers = {
          'Authorization': user.token_type + ' ' + user.access_token,
          'Accept': 'application/json'
        };
        const request = {
            method: 'GET',
            headers: headers
        };
        return fetch(url, request)
        .then(response => {
          return new Promise((resolve, reject) => {
            if (response.status === 401) {
              let err = new Error("Unauthorized");
              reject(err);
            }
            if (response.status === 500) {
              let err = new Error("Critical");
              reject(err);
            }
            if ((response.status >= 200 && response.status < 300) || response.status === 400) {
              response.json().then(json => {
                console.log(json);
                resolve(json);
              });
            }
          });
        });
      } 
      

      传奇

      export function* getMainScreenInfoSaga() {
        try {
          const user = yield select(getUser);
          const userInfo = yield select(getUserInfo);
          if (userInfo) {
            yield put({ type: types.NET_LOAD_USER_DATA });
          } else {
            yield put({ type: types.NET_INIT });
          }
          const info = yield all({
            user: call(getInfo, user),
            apartments: call(getUserApartments, user),
            accounts: call(getUserAccounts, user),
            counters: call(getCounters, user)
          });
          const ui = yield select(getUi);
          if (!ui) {
            yield put({ type: types.NET_LOAD_UI });
            const ui = yield all({
              apartmentTypes: call(getApartmentTypes, user),
              serviceTypes: call(getServiceTypes, user),
              counterTypes: call(getCounterTypes, user),
            });
            yield put({ type: types.GET_UI_SUCCESS, ui });
          }
          yield put({ type: types.GET_MAIN_SCREEN_INFO_SUCCESS, info });
          yield put({ type: types.NET_END });
      
        } catch (err) {
      
          if (err.message === "Unauthorized") {
            yield put({ type: types.LOGOUT });
            yield put({ type: types.NET_END });
          }
          if (err.message === "Critical") {
            window.alert("Server critical error");
            yield put({ type: types.NET_END });
          }
      
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2017-06-07
        • 2016-09-01
        • 2019-06-14
        • 2018-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多