Pare um cano no meio

Dec 15 2020

Em meu aplicativo angular, tenho um interceptor de atualização de token que intercepta erros 401-Unauthorized, tenta atualizar um token de acesso e executa a solicitação http original novamente.

O endpoint de atualização de token também pode falhar em alguns casos e retornar um erro conhecido (no meu caso, é um erro 400 invalid_grant).

A parte do interceptor que atualiza o token:

 return this.httpClient
  .request('POST', "token", { observe: 'response' })
  .pipe(
    map(response => {
      // some logic when the token was refreshed successfully
    }),
    catchError(err => {
      this.routingService.navigateToLoginNoQuery();
      return throwError(err); // a 400 - invalid_grant error
    })
  );

Quando isso acontece, ele chega a um operador catchError, que faz 2 coisas: logout (com redirecionamento para a página de login) e retorna throwError (erro), esse erro que ele lança é o erro 400-Invalid_grant.

Isso faz com que o novo erro chegue a alguns operadores catchError nas funções que originalmente acionaram o token de atualização.

Apenas para contexto: eu registro os erros nos blocos catch e quero evitar o registro desse tipo de erro, porque eles geralmente significam apenas que o token do usuário expirou.

O que eu gostaria de fazer é parar de alguma forma essa cadeia de operadores e apenas redirecionar para a página de login.

É possível parar um tubo no meio e evitar chegar a um catchError externo e ao próximo operador no tubo?

Respostas

1 GuerricP Dec 14 2020 at 23:25

Você pode retornar um vazio Observablepara evitar que eventos ou erros atinjam um componente que será destruído:

return this.httpClient
  .request('POST', "token", { observe: 'response' })
  .pipe(
    map(response => {
      // some logic when the token was refreshed successfully
    }),
    catchError(err => {
      this.routingService.navigateToLoginNoQuery();
      return EMPTY;
    })
  );
MishaBorisov Dec 14 2020 at 23:30

Se entendi a pergunta correta, talvez um operador takeUntil possa ajudá-lo. O exemplo ficará mais ou menos assim:

export class MyHttpInterceptor implements HttpInterceptor {
  anyError = new Subject();

  intercept(req: HttpRequest, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req)
      .pipe(
         takeUntil(this.anyError),
         catchError(err => {
             this.anyError.next();
             ...
         }
      )
  }
}