简体   繁体   中英

How can I escape a catch chain early with rxjs?

I made a wrapper for accessing the 'rxjs/observable/dom/ajax' functions with an authentication header. That part went ok.

export const authjax = {
  create: urlOrRequest => ajax(typeof urlOrRequest === 'string'
    ? {url: urlOrRequest, headers: authHeader()} : _.merge({}, urlOrRequest, {headers: authHeader()})
  ),
  get: (url, headers = {}) => ajax.get(url, {...headers, ...authHeader()}),
  post: (url, body = {}, headers = {}) => ajax.post(url, body, {...headers, ...authHeader()}),
  put: (url, body = {}, headers = {}) => ajax.put(url, body, {...headers, ...authHeader()}),
  patch: (url, body = {}, headers = {}) => ajax.patch(url, body, {...headers, ...authHeader()}),
  getJSON: (url, headers = {}) => ajax.getJSON(url, {...headers, ...authHeader()})
};

So, this is getting used in place of ajax. Here's an example of it getting used in an epic.

export const getBatchesEpic = (action$, store) =>
  action$.ofType(actions.GET_BATCHES)
    .switchMap(action => {
      const {paginate, refresh} = action;
      const paginatorNext = _.get(store.getState(), 'manager.paginator.batches.next');
      const usePaginatorNext = paginatorNext && paginate && !refresh;
      return authjax.get(usePaginatorNext ? paginatorNext : `${API_URL}/batches/`)
        .concatMap(({response}) => {
          const incompleteBatches = response.results.filter(batch => !batch.completed);
          const checkBatchActions = incompleteBatches.map(batch => checkBatch(batch.id));
          return [
            {type: actions.BATCHES_RECEIVED, data: response, next: response.next, paginate},
            ...checkBatchActions
          ];
        })
        .catch(error => Observable.of({type: actions.GET_BATCHES_ERROR, error})
      );
    });

Now, I'm in the first stage of trying to make a simple test pass using just the authjax.get method. I want 401 responses from the server to get caught by authjax, and for authjax to return a logout action, as well as cancel any following chained methods.

This is my latest attempt for authjax.get . It works for successful ajax responses, and for non 401 ajax responses. It doesn't return anything, even the logout action, when it hits the 401.

export const authjax = {
  create: urlOrRequest => ajax(typeof urlOrRequest === 'string'
    ? {url: urlOrRequest, headers: authHeader()} : _.merge({}, urlOrRequest, {headers: authHeader()})
  ),
  get: (url, headers = {}) => {
    const call = ajax.get(url, {...headers, ...authHeader()});

    return call.catch(error => {
      if (_.get(error, 'status') === 401) {
        console.log('401!!!', Observable.of({type: actions.LOGOUT}));
        return Observable.of({type: actions.LOGOUT}).ignoreElements();
      }
      return call;
    });

  },
  post: (url, body = {}, headers = {}) => ajax.post(url, body, {...headers, ...authHeader()}),
  put: (url, body = {}, headers = {}) => ajax.put(url, body, {...headers, ...authHeader()}),
  patch: (url, body = {}, headers = {}) => ajax.patch(url, body, {...headers, ...authHeader()}),
  getJSON: (url, headers = {}) => ajax.getJSON(url, {...headers, ...authHeader()})
};

I'm reading rxjs docs, but finding that there is an assumption that you would never need to switch out or cancel a stream in a stream. I know there are maybe options where I could handle more in the Epic itself, but I want authjax to be able to take care of its own authentication concerns so I can use it as an import across multiple apps using the same auth.

This is the answer that I have come up with for now.

export const wrapAjax = ({method, args = {}}) => {
  const headers = {...args.headers || {}, ...authHeader()};

  const call = method
    ? ajax[method](..._.values(_.omit(args, ['headers'])), headers)
    : ajax(_.merge({}, args, {headers}));

  return call.catch(error => {
    if (_.get(error, 'status') === 401) {
      return Observable.throw({...error, action: {type: actions.AUTH_401, error}}).ignoreElements();
    }
    return call;
  });
};

It stops any chain methods if the status is a 401, and hits any later catch statements. The catch statements in the epic sadly have to dispatch the returned action attribute. Not stoked but this is how I ended up moving on for now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM