【发布时间】:2019-11-15 08:38:03
【问题描述】:
我正在开发一个使用 API 的网站。
问题
我所有的 API 传奇都是这样的:
export function* login(action) {
const requestURL = "./api/auth/login"; // Endpoint URL
// Select the token if needed : const token = yield select(makeSelectToken());
const options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + btoa(JSON.stringify({ login: action.email, password: action.password })),
}
};
try {
// The request helper from react-boilerplate
const user = yield call(request, requestURL, options);
yield put(loginActions.loginSuccess(user.token);
yield put(push('/'));
} catch (err) {
yield put(loginActions.loginFailure(err.detailedMessage));
yield put(executeErrorHandler(err.code, err.detailedMessage, err.key)); // Error handling
}
}
我的所有 sagas 都有相同的模式:
- 如果我需要在 saga 开始时调用私有函数,请选择令牌
const token = yield select(makeSelectToken());
- 处理捕获部分的错误
export const executeErrorHandler = (code, detailedMessage, key) => ({
type: HTTP_ERROR_HANDLER, status: code, detailedMessage, key
});
export function* errorHandler(action) {
switch (action.status) {
case 400:
yield put(addError(action.key, action.detailedMessage));
break;
case 401:
put(push('/login'));
break;
//other errors...
}
}
export default function* httpError() {
yield takeLatest(HTTP_ERROR_HANDLER, errorHandler);
}
我想出的解决方案
删除令牌部分和错误处理部分并将它们放入调用助手中:
export function* login(action) {
const url = `${apiUrl.public}/signin`;
const body = JSON.stringify({
email: action.email,
password: action.password,
});
try {
const user = yield call(postRequest, { url, body });
yield put(loginSuccess(user.token, action.email));
yield put(push('/'));
} catch (err) {
yield put(loginFailure());
}
}
// post request just call the default request with a "post" method
export function postRequest({ url, headers, body, auth = null }) {
return request(url, 'post', headers, body, auth);
}
export default function request(url, method, headers, body, auth = null) {
const options = { method, headers, body };
return fetch(url, addHeader(options, auth)) // add header will add the token if auth == true
.then(checkStatus)
.then(parseJSON)
.catch(handleError); // the error handler
}
function handleError(error) {
if (error.code === 401) {
put(push('/login')); // <-- Here this doesn't work
}
if (error.code == 400) {
displayToast(error);
}
}
function addHeader(options = {}, auth) {
const newOptions = { ...options };
if (!options.headers) {
newOptions.headers = {
Accept: 'application/json',
'Content-Type': 'application/json',
...options.headers,
};
}
if (auth) {
const token = yield select(makeSelectToken()); // <-- here it doesn't work
newOptions.headers.Authorization = `Bearer ${auth}`;
}
return newOptions;
}
我知道解决方案是在生成器函数、副作用、yield 调用/选择之间,但我尝试了很多方法都不起作用。例如,如果我将所有内容包装在生成器函数中,则在代码继续并调用 API 后执行令牌加载。
您的帮助将不胜感激。
【问题讨论】:
-
addHeader 不是生成器函数,所以我不相信你可以使用yield。
-
@MattSugden 我尝试将 addHeader 转换为生成器函数,然后也转换为请求函数。传奇仍在继续,无需等待选择令牌。
-
那是因为你没有另一个收益率低于它吗?传奇没有下一项等待
-
@MattSugden 我尝试在 addHeader 中添加 const token = yield select(makeSelectToken());,然后调用 addHeader( options, auth).next().value 在 fetch 中。但是,addHeader 没有在里面有带有令牌的对象,而是在不等待令牌的情况下返回了对象。
标签: reactjs fetch generator redux-saga yield