tutoriales.com

Domina las Interceptor HTTP en Angular: Guía Definitiva de Seguridad y Caché

Descubre cómo utilizar los interceptores HTTP en Angular para centralizar la lógica de tus peticiones de red, añadir cabeceras de autenticación, reintentar peticiones fallidas y optimizar el rendimiento de tu aplicación web.

Intermedio12 min de lectura18 views
Reportar error

Introducción a los Http Interceptors en Angular 🚀

En el desarrollo moderno de aplicaciones web, la comunicación con servidores externos a través de APIs REST es una tarea constante y fundamental. A medida que una aplicación Angular crece, la necesidad de manipular estas peticiones y respuestas HTTP se vuelve compleja. ¿Necesitas adjuntar un token de autorización en cada llamada? ¿Quieres mostrar un indicador global de carga? ¿O tal vez manejar errores 401 de forma centralizada sin repetir código en cada servicio?

Aquí es donde entran en juego los Http Interceptors. Los interceptores son una pieza clave en la arquitectura de Angular que te permiten inspeccionar y transformar las peticiones HTTP Intermedio antes de que sean enviadas por el cliente, así como interceptar las respuestas entrantes antes de que lleguen a los componentes.

💡 Consejo: Piénsalo como los puestos de control en un aeropuerto. Ninguna maleta (petición) sale o entra al país (servidor) sin antes pasar por el control de seguridad (el interceptor) para ser revisada, sellada o modificada.

¿Qué es un Http Interceptor y cómo funciona? 🛠️

Un interceptor implementa la interfaz HttpInterceptor (o utiliza la nueva función basada en HttpInterceptorFn introducida en Angular 15/16). Su trabajo principal es actuar como un intermediario entre tu aplicación y el backend.

Flujo de Interceptores HTTP Componente Auth Logging Cache Backend PETICIÓN (Request) RESPUESTA (Response) Salida Entrada

Cuando realizas una petición usando HttpClient, esta pasa a través de una cadena de interceptores antes de ser enviada a la red. Cada interceptor puede:

  • Clonar la petición y modificar sus cabeceras o parámetros.
  • Manejar la respuesta y transformar los datos.
  • Capturar errores de la red y tomar decisiones (como refrescar tokens).
  • Bloquear o cancelar peticiones bajo ciertas condiciones.

Comparativa de Enfoques: Clásico vs Funcional

CaracterísticaEnfoque de Clase (Legacy)Enfoque Funcional (Moderno - Angular 15+)
---------
SintaxisClases con @Injectable()Funciones puras independientes
ConfiguraciónProveedores en app.module.tsprovideHttpClient(withInterceptors([...]))
---------
LegibilidadVerbosa, requiere boilerplateLimpia, concisa y fácil de probar
Inyección de DependenciasConstructor injectioninject() function

Configurando tu Primer Interceptor Funcional 🎯

Vamos a crear un interceptor moderno que añada una cabecera personalizada a todas las peticiones salientes, simulando una cabecera de versión de API o un identificador de cliente.

Paso 1: Creación del archivo

Crea un archivo llamado api-prefix.interceptor.ts en la carpeta de tus interceptores:

import { HttpInterceptorFn } from '@angular/common/http';

export const apiPrefixInterceptor: HttpInterceptorFn = (req, next) => {
  // Clonamos la petición para añadir una nueva cabecera
  const apiReq = req.clone({
    setHeaders: {
      'X-Client-Version': '1.0.0',
      'X-App-Source': 'Angular-Web'
    }
  });

  // Pasamos la petición modificada al siguiente manejador
  return next(apiReq);
};

Paso 2: Registro en el Proveedor Global

Para que Angular reconozca este interceptor, debes registrarlo en la configuración de tu aplicación, típicamente en app.config.ts:

import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { apiPrefixInterceptor } from './interceptors/api-prefix.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [ 
    provideHttpClient(
      withInterceptors([apiPrefixInterceptor])
    )
  ]
};
📌 Nota: El orden en el array `withInterceptors` importa. Se ejecutan en el orden exacto en que son declarados de izquierda a derecha para las peticiones, y en orden inverso para las respuestas.

Casos de Uso Avanzados en Aplicaciones Reales 🔥

Los interceptores brillan cuando se implementan en escenarios del mundo real. A continuación, veremos tres de los patrones más demandados en la industria.

1. Gestión de Autenticación con Token JWT

Uno de los usos más comunes es inyectar automáticamente el token de acceso (Bearer Token) almacenado en el almacenamiento local o en un servicio de estado.

import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  const authService = inject(AuthService);
  const token = authService.getToken();

  if (token) {
    const cloned = req.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
    return next(cloned);
  }

  return next(req);
};

2. Manejo Global de Errores y Reintentos

Imagina que tu aplicación sufre una breve caída de red. Puedes utilizar operadores de RxJS como retry o catchError dentro del interceptor para gestionar los fallos de manera elegante.

import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { catchError, retry, throwError } from 'rxjs';
import { inject } from '@angular/core';
import { NotificationService } from '../services/notification.service';

export const errorHandlingInterceptor: HttpInterceptorFn = (req, next) => {
  const notifications = inject(NotificationService);

  return next(req).pipe(
    retry(2), // Reintenta la petición hasta 2 veces antes de fallar
    catchError((error: HttpErrorResponse) => {
      let errorMsg = 'Ocurrió un error desconocido';
      
      if (error.error instanceof ErrorEvent) {
        // Error del lado del cliente
        errorMsg = `Error: ${error.error.message}`;
      } else {
        // Error del lado del servidor
        errorMsg = `Código de servidor: ${error.status}, Mensaje: ${error.message}`;
        
        if (error.status === 401) {
          notifications.show('Sesión expirada. Por favor, inicia sesión nuevamente.');
          // Aquí podrías disparar la redirección al login
        }
      }
      
      notifications.show(errorMsg);
      return throwError(() => new Error(errorMsg));
    })
  );
};

3. Sistema de Caché para Peticiones GET

Optimizar el rendimiento evitando llamadas redundantes al servidor es crucial. Podemos implementar una caché simple basada en memoria usando un objeto Map.

import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
import { of, tap } from 'rxjs';

const cache = new Map<string, HttpResponse<any>>();

export const cacheInterceptor: HttpInterceptorFn = (req, next) => {
  // Solo aplicamos caché a peticiones GET
  if (req.method !== 'GET') {
    return next(req);
  }

  const cachedResponse = cache.get(req.urlWithParams);
  if (cachedResponse) {
    // Retornamos la respuesta desde la caché envuelta en un observable
    return of(cachedResponse);
  }

  return next(req).pipe(
    tap(event => {
      if (event instanceof HttpResponse) {
        cache.set(req.urlWithParams, event);
      }
    })
  );
};

Buenas Prácticas y Consejos de Rendimiento ⚡

Para mantener tu código limpio y evitar comportamientos extraños en tu aplicación, ten en cuenta las siguientes recomendaciones:

  • Evita bucles infinitos: Si un interceptor realiza una petición HTTP interna (por ejemplo, para refrescar un token), asegúrate de usar un cliente HTTP alternativo o excluir dicha ruta para evitar que el interceptor se llame a sí mismo recursivamente.
  • Inmutabilidad: Recuerda que los objetos HttpRequest son inmutables. Siempre debes utilizar el método .clone({...}) para modificar cualquier propiedad.
  • Orden de ejecución: Coloca los interceptores de registro (logging) y métricas al principio de la cadena, los de seguridad/autenticación en el medio, y los de manejo de errores o transformación de datos al final.
⚠️ Advertencia: No abuses del almacenamiento en caché a nivel de interceptor para datos altamente dinámicos o sensibles, ya que podrías mostrar información obsoleta al usuario.

Preguntas Frecuentes (FAQs) 📖

¿Puedo excluir un interceptor en una petición específica? Sí. Puedes pasar contextos personalizados a la petición utilizando `HttpContext` y `HttpContextToken`, lo cual te permite evaluar dentro del interceptor si debe aplicarse o no a esa llamada en particular.
¿Los interceptores funcionan con SSR (Server-Side Rendering)? Sí, operan perfectamente en entornos de servidor, pero debes tener cuidado al acceder a objetos globales del navegador como `localStorage` o `window`, utilizando las herramientas de inyección de plataforma de Angular (`isPlatformBrowser`).

Conclusión y Siguientes Pasos 🎓

Los interceptores HTTP son una herramienta indispensable en el arsenal de cualquier desarrollador de Angular. Te permiten mantener el principio de responsabilidad única (Single Responsibility Principle) en tus componentes y servicios, centralizando preocupaciones transversales como seguridad, caché y manejo de errores.

Te invitamos a refactorizar tu próxima aplicación Angular adoptando el enfoque funcional de interceptores y combinándolos con la potencia de RxJS.

Paso 1: Identifica lógica repetitiva en tus llamadas HTTP.
Paso 2: Diseña un interceptor funcional modular.
Paso 3: Regístralo con provideHttpClient(withInterceptors([...])).
Paso 4: Escribe pruebas unitarias para validar su comportamiento.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!