如何使用 Redux-Saga 处理 fetch() 响应中的错误?
How to handle errors in fetch() responses with Redux-Saga?
我尝试使用 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_SUCCEEDED', user: undefined}
,而我期望 {type: 'LOG_IN_FAILED', error: 'Unauthorized'}
。我的错误在哪里?如何使用 Redux-Saga 正确处理错误?
不要在您的 fetchUser
方法和传奇中处理 then
和 error
。由于您已经 try
/catch
进入您的传奇,您可以在那里处理它。
例子
传奇
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 有点尴尬,因为它 returns 在您发出请求时能够做出 then
响应。那里有很多图书馆;就我个人而言,我更喜欢 axios
,默认情况下 returns json。
如果你想让 if 语句验证响应状态 if(res.status >= 200 && res.status < 300) {
你需要把它放在你定义 res 的第一个 promise 中,它目前在 res.json()
[= 的 resolved promise 中13=]
.then(res => {
if (res.status >= 200 && res.status < 300) {
res.json().then(json => {
return json
}
})
})
如果您需要在一个 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 });
}
}
}
我尝试使用 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_SUCCEEDED', user: undefined}
,而我期望 {type: 'LOG_IN_FAILED', error: 'Unauthorized'}
。我的错误在哪里?如何使用 Redux-Saga 正确处理错误?
不要在您的 fetchUser
方法和传奇中处理 then
和 error
。由于您已经 try
/catch
进入您的传奇,您可以在那里处理它。
例子
传奇
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 有点尴尬,因为它 returns 在您发出请求时能够做出 then
响应。那里有很多图书馆;就我个人而言,我更喜欢 axios
,默认情况下 returns json。
如果你想让 if 语句验证响应状态 if(res.status >= 200 && res.status < 300) {
你需要把它放在你定义 res 的第一个 promise 中,它目前在 res.json()
[= 的 resolved promise 中13=]
.then(res => {
if (res.status >= 200 && res.status < 300) {
res.json().then(json => {
return json
}
})
})
如果您需要在一个 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 });
}
}
}