Reputation: 3393
I'm using an interceptor to handle unauthorised HTTP requests, which will either show a modal or route to the login page. When the interceptor handles the error I don't want the service or component that made the original request to handle the error as well. I do want the component or services error handling to kick in for any errors which are not 401s.
In my interceptor I'm catching the error with RxJS catchError
and checking the error details. Where it's not going to be handled by the interceptor I'm just re-throwing it with throwError
which seems to work okay. The problem is where I need to stop the error propagating. If I return nothing I get the following error:
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
Returning an empty observable using of()
almost works, but I get the following error in the console when the application loads.
ERROR Error: Uncaught (in promise): EmptyError: no elements in sequence
I can't work out why, or how to fix it. How should I terminate execution in an interceptor, essentially completing the observable? Here's my code:
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor (
private router: Router,
private sessionService: SessionService
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone()
return next.handle(request).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401 && this.router.url === '/') {
this.router.navigateByUrl('/login')
} else if (err.status === 401 && !/^\/activate-account/.test(this.router.url) && !/^\/log-?[i,I]n/.test(this.router.url)) {
this.sessionService.setSessionExpired()
}
return of()
} else {
return throwError(err)
}
})
)
}
}
Upvotes: 2
Views: 3679
Reputation: 3393
It seems that the problem was due to navigating away before returning an empty observable. I've moved the return statements into the specific if conditions so I can return throwError(err)
after calling this.router.navigateByUrl
, otherwise I return an empty observable. I'm also using the EMPTY
observable constant (as suggested by Suresh Kumar Ariya)
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor (
private router: Router,
private sessionService: SessionService
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone()
return next.handle(request).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401 && this.router.url === '/') {
this.router.navigateByUrl('/login')
return throwError(err)
} else if (err.status === 401 && !/^\/activate-account/.test(this.router.url) && !/^\/log-?[i,I]n/.test(this.router.url)) {
this.sessionService.setSessionExpired()
return EMPTY
} else {
return throwError(err)
}
} else {
return throwError(err)
}
})
)
}
}
Upvotes: 0
Reputation: 9774
Why can't you try Observable.empty(). Which will stop propagation and return the handler back to the calling service.
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor (
private router: Router,
private sessionService: SessionService
) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
request = request.clone()
return next.handle(request).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401 && this.router.url === '/') {
this.router.navigateByUrl('/login')
} else if (err.status === 401 && !/^\/activate-account/.test(this.router.url) && !/^\/log-?[i,I]n/.test(this.router.url)) {
this.sessionService.setSessionExpired()
}
return Observable.empty();
} else {
return throwError(err)
}
})
)
}
}
Upvotes: 1
Reputation:
The interceptor expects a return statement that you transfer with catchError
. If you don't, you don't respect the signature of the interceptor, thus your error.
To avoid that, always return a thrown observable. You can return throwError(undefined)
, and in your error handling, if you see the error is undefined, you simply don't treat it. You can even go further and use Rx to filter the undefined errors with .pipe(filter(err => !!err))
.
Upvotes: 0