如何处理传奇中的常见提取操作

问题描述 投票:1回答:1

我正在开发使用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都有相同的模式:

  • 如果需要在传奇开始时调用私有函数,请选择令牌

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之后执行令牌加载。

您的帮助将不胜感激。

reactjs fetch generator redux-saga yield
1个回答
0
投票

您需要通过生成器函数运行任何和所有效果(例如yield select),因此您将一直需要生成器,直到调用堆栈中产生效果的点。考虑到我会尽量提高这些要求。我认为除了getRequest之外,您还可能还有putRequestpostRequest等,因此,如果您要避免重复yield select,则可以在request中进行。我无法完全测试您的代码段,但我认为这应该可以:

export function* postRequest({ url, headers, body, auth = null }) {
  return yield call(request, url, 'post', headers, body, auth); // could yield directly but using `call` makes testing eaiser
}

export default function* request(url, method, headers, body, auth = null) {
  const options = { method, headers, body };
  const token = auth ? yield select(makeSelectToken()) : null;
  try {
      const response = yield call(fetch, url, addHeader(options, token));
      const checkedResponse = checkStatus(response);
      return parseJSON(checkedResponse);
  } catch (e) {
     const errorEffect = getErrorEffect(e); // replaces handleError
     if (errorEffect) {
        yield errorEffect;
     }
  }
}

function addHeader(options = {}, token) {
  const newOptions = { ...options };
  if (!options.headers) {
    newOptions.headers = {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      ...options.headers,
    };
  }

  if (token) {
    newOptions.headers.Authorization = `Bearer ${token}`;
  }

  return newOptions;
}

function getErrorEffect(error) {
  if (error.code === 401) {
    return put(push('/login')); // returns the effect for the `request` generator to yeild
  }

  if (error.code == 400) {
    return displayToast(error); // assuming `displayToast` is an effect that can be yielded directly
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.