tomz
tomz

Reputation: 341

Handling HTTP redirection status codes

I'm trying to handle HTTP redirection status codes (e.g. 302 redirect when the session timed out) and I don't know if there is a generic way to handle specific response codes using redux-observable? The problem I have now is that the browser follows location specified in 302 response and I can only tap into subsequent 200 response for the login page. I've got a bit of a hack now where I detect the word 'login' in response URL and redirect to login page using window.location object. I have to do this in every epic.

Here's what I've got:

    export const getData = (action$) => {
    return action$.pipe(
        ofType(GET_DATA_REQUEST),
        mergeMap(action => {
            return ajax(options).pipe(
                map((response) => response.originalEvent.currentTarget.responseURL.endsWith('login') ? window.location.href = 'login' : getDataSuccess(response.response)),
                catchError(error => of(getDataFailure(error)))
            );
        }),
        catchError(error => of(getDataFailure(error)))
    );
};

Does anyone know any better ways of handling this problem where I wouldn't have to repeat it for all new epics?

Upvotes: 1

Views: 762

Answers (1)

seniorquico
seniorquico

Reputation: 1459

The ajax operations wrap XMLHttpRequest, and XMLHttpRequest automatically follows redirects. While the redirect cannot be prevented, it can be detected. Here's another example of detecting the redirect:

export const getData = action$ =>
  action$.pipe(
    ofType(GET_DATA_REQUEST),
    mergeMap(action =>
      ajax(options).pipe(
        mergeMap(response => {
          // Navigate to login if the request was successful but redirected
          if (response.status >= 200 && response.status < 300 && response.responseURL !== options.url) {
            window.location.href = 'login'
            return empty()
          }

          return of(getDataSuccess(response.response))
        })
      )
    )
  )

If you want to reuse this logic in multiple epics, simply export it as a reusable function:

export const ajaxWithLoginRedirect = options =>
  ajax(options).pipe(
    mergeMap(response => {
      // Navigate to login if the request was successful but redirected
      if (response.status >= 200 && response.status < 300 && response.responseURL !== options.url) {
        window.location.href = 'login'
        return empty()
      }

      // Return the raw response
      return of(response)
    })
  )

export const getData = action$ =>
  action$.pipe(
    ofType(GET_DATA_REQUEST),
    mergeMap(action =>
      ajaxWithLoginRedirect(options).pipe(
        // This is only called if we did not redirect
        map(response => getDataSuccess(response.response))
      )
    )
  )

Take note that the fetch API does support handling redirects manually (the response object you get back will have the 3xx status code). There are a number of trade-offs between XMLHttpRequest and fetch, so I would research that if not automatically following redirects were preferable in your application.

Upvotes: 1

Related Questions