Capítulo 15: Comunicação HTTP com HttpClient e Functional Interceptors

Especialização em Front-End com Angular • Angular 18+ • Signals, Standalone Architecture, RxJS, NgRx e SSR


🗺️ Mapa Conceitual da Pipeline de Interceptação HTTP

flowchart LR
    App["Aplicação / Serviço Angular (HttpClient.get)"] --> Req["HttpRequest"]

    subgraph INTERCEPTORS["Pipeline de Interceptores Funcionais (HttpInterceptorFn)"]
        I1["authInterceptor (Injeta Bearer JWT)"]
        I2["loggingInterceptor (Mede Latência de Rede)"]
        I3["errorInterceptor (Captura 401 / 403 / 500)"]
    end

    Req --> I1 --> I2 --> I3
    I3 --> Backend["API REST Externa / Backend"]

    Backend --> Res["HttpResponse / HttpErrorResponse"]
    Res --> I3 --> I2 --> I1
    I1 --> App

    style App fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style INTERCEPTORS fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style Backend fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style Res fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. A Nova Arquitetura HTTP do Angular 18+

No Angular clássico, a comunicação com APIs externas dependia do HttpClientModule e da injeção de interceptores baseados em classes complexas registradas com HTTP_INTERCEPTORS e multi: true.

No Angular Standalone moderno:

  • O módulo foi substituído pelo provedor funcional provideHttpClient().
  • O mecanismo de transporte agora pode utilizar a API nativa do navegador com withFetch(), fundamental para Server-Side Rendering (SSR) e streaming.
  • Interceptores são agora funções puras tipadas (HttpInterceptorFn), tornando a pipeline muito mais simples de testar, compor e auditar.

⚙️ 2. Configuração do provideHttpClient no app.config.ts

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHttpClient, withInterceptors, withFetch } from '@angular/common/http';
import { authInterceptor } from './core/interceptors/auth.interceptor';
import { loggingInterceptor } from './core/interceptors/logging.interceptor';
import { errorInterceptor } from './core/interceptors/error.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(
      withFetch(), // Utiliza a Fetch API nativa em vez de XMLHttpRequest (essencial para SSR)
      withInterceptors([authInterceptor, loggingInterceptor, errorInterceptor])
    )
  ]
};

🛠️ 3. Implementação dos Interceptores Funcionais Padrão Ouro

3.1 Interceptor de Autenticação (authInterceptor)

Injeta o token JWT no cabeçalho Authorization: Bearer <token> de todas as requisições direcionadas para APIs seguras:

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

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

  // Se houver token, clona a requisição e adiciona o header de autorização
  if (token) {
    const reqAutenticada = req.clone({
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
    return next(reqAutenticada);
  }

  // Se não houver token, segue o fluxo original
  return next(req);
};

3.2 Interceptor de Tratamento Global de Erros (errorInterceptor)

Captura respostas com código HTTP de erro (401 Não Autorizado, 403 Proibido, 500 Erro Interno) e orquestra a notificação ou deslogamento:

// core/interceptors/error.interceptor.ts
import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { catchError } from 'rxjs/operators';
import { throwError } from 'rxjs';

export const errorInterceptor: HttpInterceptorFn = (req, next) => {
  const router = inject(Router);

  return next(req).pipe(
    catchError((erro: HttpErrorResponse) => {
      if (erro.status === 401) {
        console.warn('Sessão expirada. Redirecionando para login...');
        router.navigate(['/login']);
      } else if (erro.status === 403) {
        console.error('Acesso negado ao recurso solicitado.');
      } else if (erro.status >= 500) {
        console.error('Instabilidade no servidor externo. Tente novamente mais tarde.');
      }

      // Propaga o erro tratado para quem chamou
      return throwError(() => erro);
    })
  );
};

🏷️ 4. Parametrização com HttpContext e HttpContextToken

Muitas vezes, uma requisição específica não deve receber o token de autenticação (por exemplo, a chamada pública de login) ou não deve exibir o loader global. O HttpContext permite passar metadados tipados:

import { HttpContextToken, HttpContext, HttpClient } from '@angular/common/http';
import { inject } from '@angular/core';

// 1. Criação do Token de Contexto
export const IGNORAR_AUTH = new HttpContextToken<boolean>(() => false);

// 2. Serviço consumindo com contexto customizado
export class ApiService {
  private http = inject(HttpClient);

  loginPublico(credenciais: any) {
    return this.http.post('/api/auth/login', credenciais, {
      context: new HttpContext().set(IGNORAR_AUTH, true) // Informa ao interceptor para pular
    });
  }
}

No interceptor:

export const authInterceptor: HttpInterceptorFn = (req, next) => {
  if (req.context.get(IGNORAR_AUTH)) {
    return next(req); // Pula injeção de token
  }
  // ...
};

⚖️ 5. Comparativo: XMLHttpRequest vs withFetch()

Critério provideHttpClient() Padrão (XHR) withFetch() Moderno
API do Navegador XMLHttpRequest legada window.fetch() moderna
Streaming de Respostas Não suportado de forma eficiente Nativo (Server-Sent Events e streams)
Execução em SSR Requer emulação de XHR no Node.js Nativo no runtime do Node.js moderno
Cancelamento de Requisição abort() via XHR AbortController nativo
Recomendação Angular 18+ Legado Altamente Recomendado

⚠️ 6. Armadilhas Comuns e Anti-Patterns

  1. Mutar o Objeto req Diretamente:
    • As requisições HTTP do Angular são objetos imutáveis. Fazer req.headers.append(...) não funciona!
    • Correção: Utilize sempre req.clone({ ... }).
  2. Esquecer de Adicionar withFetch() em Aplicações SSR:
    • Em projetos com Server-Side Rendering, omitir withFetch() pode causar inconsistências na serialização de requisições durante a hidratação.

🛡️ 7. Boas Práticas e Segurança Corporativa

  • Sanitização de URLs de Destino: Valide a URL da requisição antes de injetar o token de autorização. Nunca envie seu token Bearer corporativo para domínios ou CDNs de terceiros.
  • Tipagem Fiel dos DTOs: Tipar todas as respostas com generics estritos: this.http.get<ClienteDTO[]>('/api/clientes').

📋 8. Cheat-Sheet: Resumo Executivo

// Interceptor funcional
export const meuInterceptor: HttpInterceptorFn = (req, next) => {
  const clonada = req.clone({ setHeaders: { 'X-App-Version': '1.0' } });
  return next(clonada);
};

// app.config.ts
provideHttpClient(withFetch(), withInterceptors([meuInterceptor]))