angular tutorial

Hace unos meses me tocó hacer un proyecto donde tenía que hacer una validación del usuario, aparte para todas las llamadas necesitaba enviar el token por seguridad, pero el problema era dos cosas

  • eran más de 50 llamadas api rest
  • necesitaba identificar cuando las llamadas daban error para pedir un token nuevo

la forma más sencilla era hacer lo siguiente en angular:

let headers = new HttpHeaders().set('header1', hvalue1); // se crea el header object
headers = headers.append('token', value token); // se agrega nuevos header
headers = headers.append('header2', hvalue3); 

return this._http.get<any[]>(url, { headers: headers }) // se agregaq nuestro header en la llamada

la problemática es que si son 50 o 60 llamadas, necesitamos poner esto en cada llamada, y en caso de cambiar el nombre del header, o el método tenemos que editar 60 llamadas.

obviamente no es lo mas optimo así que necesitamos usar un interceptor.

Que es un interceptor

De manera muy sencilla y en pocas líneas diré que un interceptor en angular es un archivo o trigger que estará monitoreando todas las llamadas http que se hacen en la página, este analiza las llamadas y si cumple la condición va a hacer algo.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { Router } from '@angular/router';
import { EMPTY } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AuthInterceptorServiceService implements HttpInterceptor {
  url401='';
  reload = 0;
  constructor(private router: Router) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token: string = localStorage.getItem('tk');

    let request = req;

    if (token) {
      request = req.clone({
        setHeaders: {
          authorization: token
        }
      });
    }
   
    if(this.reload > 30){
      setTimeout(x => {
        this.reload = 0;

      },10000)
      return EMPTY
    }else {
      return next.handle(request).pipe(
        tap((event: HttpEvent<any>) => {
          if (event instanceof HttpResponse && event.status === 200) {
            this.url401 = '';
            this.reload = 0;
          }
        }),
        catchError((err: HttpErrorResponse) => {
  
          if (err.status === 401) {
            if(this.url401 == request.url){
              if(this.reload > 12){
                this.reload = 0;
              }else {
                this.reload ++;
              }
              
            }else{
              this.url401 = request.url;
              this.reload = 0;
            }
            
  
            localStorage.removeItem('tk')
            if(this.router.url == '/login' || this.router.url == '/'){
            }else{
              this.router.navigateByUrl('/login');
            }
            
          }
  
          return throwError( err );
  
        })
      );
    }

    // return next.handle(request)
    
  }
}

en el app module importamos nuestro archivo y buscamos la linea donde dice providers y agregamos lo siguiente

providers: [{
    provide: HTTP_INTERCEPTORS,
      useClass: nombredelinterceptor,
      multi: true
    }]

Que hace el código interceptor

vamos a dividir en 3 secciones

import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { Router } from '@angular/router';
import { EMPTY } from 'rxjs';

@Injectable({
  providedIn: 'root'
})

usaremos las librerías de common http que son los necesarios para que podamos obtener los request y analizarlos.

empty lo usamos solo para cuando por ejemplo estamos en la página que queremos no nos está redireccionando cada x tiempo.

export class AuthInterceptorServiceService implements HttpInterceptor {
  url401='';
  reload = 0;
  constructor(private router: Router) { }
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token: string = localStorage.getItem('tk');

    let request = req;

    if (token) {
      request = req.clone({
        setHeaders: {
          authorization: token
        }
      });
    }
   
    if(this.reload > 30){
      setTimeout(x => {
        this.reload = 0;

      },10000)
      return EMPTY
}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> este es la clase que usaremos para manipular las llamadas asi que no modificamos nada.

if (token) { request = req.clone({ setHeaders: { authorization: token } }); }

este es una línea bastante importante, lo que hacemos es que httpRequest tiene una variable de clonar, que lo que hace es copiar toda la llamada y podemos modificarlo, entonces lo que haces es una vez copiado le agregamos una cabecera nueva que no teniamos que se llama authorization y el token.

esto podría quitarlo ya que lo agregue para que cada 10 seg se vuelva a activar la comprobacion http

if(this.reload > 30){ setTimeout(x => { this.reload = 0; },10000) return EMPTY

lo que hacemos es que enviamos un http vacío enviando return EMPTY para que no nos de error ni quiera redirigirnos

else {
      return next.handle(request).pipe(
        tap((event: HttpEvent<any>) => {
          if (event instanceof HttpResponse && event.status === 200) {
            this.url401 = '';
            this.reload = 0;
          }
        }),
        catchError((err: HttpErrorResponse) => {
  
          if (err.status === 401) {
            if(this.url401 == request.url){
              if(this.reload > 12){
                this.reload = 0;
              }else {
                this.reload ++;
              }
              
            }else{
              this.url401 = request.url;
              this.reload = 0;
            }
            
  
            localStorage.removeItem('tk')
            if(this.router.url == '/login' || this.router.url == '/'){
            }else{
              this.router.navigateByUrl('/login');
            }
            
          }
  
          return throwError( err );
  
        })
      );
    }

    
  }
}
return next.handle(request).pipe(
        tap((event: HttpEvent<any>) => {
          if (event instanceof HttpResponse && event.status === 200) {
            this.url401 = '';
            this.reload = 0;
          }
        }),

aquí analizamos las llamadas, next.handle es una forma de decirle al código : «vamos a revisar que trae tu llamada antes de enviarlo»

en caso que nos de un estatus 200 o correcto no hacemos nada y limpiamos nuestras variables

catchError((err: HttpErrorResponse) => {
  
          if (err.status === 401) {
            if(this.url401 == request.url){
              if(this.reload > 12){
                this.reload = 0;
              }else {
                this.reload ++;
              }
              
            }else{
              this.url401 = request.url;
              this.reload = 0;
            }
            
  
            localStorage.removeItem('tk')
            if(this.router.url == '/login' || this.router.url == '/'){
            }else{
              this.router.navigateByUrl('/login');
            }
            
          }
  
          return throwError( err );

en caso que la llamada anterior nos de un 401 (acceso denegado) comenzamos a hacer la tarea necesaria, guardamos en una variable las veces que intenta y nos da error, borramos el token que tenemos guardado y pedimos unos nuevo, en mi caso redirijo a mi login y género una nueva petición.

Con esto tenemos de forma muy fácil la forma de agregar un token a todas nuestras llamadas!

por Cesar Flores

Programador de tiempo completo, Gamer de medio tiempo y fotógrafo ocasionalmente, me gusta el front-end y mi framework favorito es angular aunque no por eso le hago el feo a un nuevo lenguaje.

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.