Identificação
- Duração: 2 horas
- Tipo: teoria aplicada e laboratório
- Entrega: fluxo HTTP resiliente e retorno acessível
- Laboratório:
../exemplos/aula-4.9/index.html
Introdução
O que é um interceptor?
Interceptor é uma função posicionada no caminho das requisições feitas pelo
HttpClient. Ela pode observar ou transformar uma requisição antes de chegar à API e
observar a resposta quando ela retorna.
componente → HttpClient → interceptor → API
componente ← Observable ← interceptor ← API
Ele funciona como um middleware do cliente Angular. Não é a API NestJS, não roda
no MySQL e não intercepta todas as requisições do navegador: apenas as que passam
pelo HttpClient configurado naquela aplicação.
Para que serve?
Interceptors removem preocupações repetidas de cada serviço:
- adicionar um identificador de correlação;
- anexar credenciais permitidas ao destino correto;
- medir duração;
- registrar falhas técnicas;
- aplicar timeout ou retry controlado;
- coordenar um indicador global de atividade.
O TaskApi continua responsável pelos endpoints de tarefas. O interceptor cuida de
uma política transversal que vale para várias chamadas.
Interceptor protege a API?
Não. Qualquer código Angular é entregue ao navegador e pode ser inspecionado ou alterado pelo usuário. A API NestJS precisa autenticar, autorizar e validar cada operação.
Angular adiciona credencial → NestJS verifica credencial → regra autoriza ação
Nunca coloque senha do MySQL, chave privada ou segredo de servidor em um interceptor.
O que é uma cadeia de interceptors?
Podemos registrar várias funções. Na ida, elas executam na ordem configurada. A resposta retorna pelas mesmas funções no sentido inverso.
requisição → correlação → autenticação → telemetria → API
resposta ← correlação ← autenticação ← telemetria ← API
Cada interceptor decide se chama next(request). Sem next, a cadeia não continua,
a menos que ele produza uma resposta sintética conscientemente, como em um cache.
O que é erro HTTP?
É uma falha observada no fluxo de uma requisição. O Angular a representa com
HttpErrorResponse, incluindo falhas de rede, timeout, parsing e respostas como 401,
404 ou 500.
Um status descreve a categoria técnica, mas a interface precisa comunicar uma ação útil: tentar novamente, revisar dados, entrar novamente ou procurar suporte.
Erro HTTP é igual a exceção global?
Não. Um 404 esperado pertence ao fluxo da chamada e deve ser tratado perto da
operação. ErrorHandler é uma última fronteira para erros inesperados que escaparam,
não um substituto para catchError.
O que acessibilidade tem a ver com erros?
Uma mensagem apenas vermelha pode não ser percebida por quem não enxerga cores ou usa leitor de tela. Retorno acessível combina texto, semântica, foco e uma próxima ação clara.
<p role="status">Tarefa salva com sucesso.</p>
<p role="alert">Não foi possível salvar. Revise a conexão.</p>
role="status" anuncia atualizações comuns de forma educada. role="alert" é
assertivo e deve ser usado com moderação para mensagens importantes.
Como isso entra na Knowledge AI?
O painel enviará requisições por uma cadeia previsível. O interceptor adicionará correlação e medirá a operação; o serviço preservará o erro; a página apresentará loading, sucesso ou falha com semântica acessível.
Objetivos
Ao final da aula, o aluno deverá conseguir:
- explicar interceptor e middleware;
- diferenciar interceptor Angular e middleware NestJS;
- criar um interceptor funcional;
- configurar
withInterceptors; - explicar a ordem de requisição e resposta;
- clonar requisições imutáveis;
- utilizar
HttpContextTokenpara políticas por requisição; - preservar erros com
throwError; - classificar rede, timeout, autenticação, cliente e servidor;
- aplicar retry apenas quando seguro;
- separar mensagem ao usuário de telemetria técnica;
- implementar loading sem contagem incorreta;
- usar status, alert e foco conscientemente;
- distinguir erro esperado de
ErrorHandlerglobal.
Pré-requisitos
- Aulas 4.1 a 4.8 concluídas;
HttpClient, Observable e operadores RxJS;- serviços e injeção de dependência;
- estados loading, success, empty e error;
- HTML semântico e navegação por teclado.
Pergunta orientadora
Como aplicar políticas comuns a todas as requisições sem esconder erros nem deixar usuários sem retorno?
Roteiro sugerido
| Etapa | Duração |
|---|---|
| Interceptor e cadeia HTTP | 25 min |
| Imutabilidade, headers e contexto | 25 min |
| Erros, timeout e retry | 30 min |
| Retorno acessível | 20 min |
| Laboratório | 15 min |
| Revisão | 5 min |
1. Forma mínima de um interceptor funcional
import { HttpInterceptorFn } from "@angular/common/http";
export const loggingInterceptor: HttpInterceptorFn = (request, next) => {
console.log(request.method, request.url);
return next(request);
};
request é a mensagem atual. next entrega essa mensagem ao próximo elemento da
cadeia e retorna um Observable de eventos HTTP.
2. Configuração explícita
import { provideHttpClient, withInterceptors } from "@angular/common/http";
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
correlationInterceptor,
authInterceptor,
telemetryInterceptor,
]),
),
],
};
Interceptors funcionais são preferidos pela documentação atual por apresentarem ordem mais previsível em configurações complexas.
3. Ordem da cadeia
Com [correlation, auth, telemetry]:
ida: correlation → auth → telemetry → backend
volta: correlation ← auth ← telemetry ← backend
Na volta, cada função observa o Observable devolvido por next.
4. Requisições são imutáveis
A maior parte de HttpRequest e HttpResponse não deve ser modificada diretamente.
Use clone:
const correlatedRequest = request.clone({
setHeaders: { "X-Correlation-ID": crypto.randomUUID() },
});
return next(correlatedRequest);
Isso torna o interceptor mais seguro quando a mesma requisição atravessa a cadeia novamente em um retry. O corpo não recebe proteção contra mutação profunda; evite alterá-lo no lugar.
5. Correlação não é autenticação
Um correlation ID liga registros do navegador, gateway e servidor para investigar uma operação. Ele não comprova identidade nem concede permissão.
X-Correlation-ID: 58fa...
Authorization: Bearer credencial-do-usuário
Cada header tem finalidade diferente.
6. Não enviar credencial para qualquer destino
const isOurApi = request.url.startsWith("/api/");
if (!isOurApi) return next(request);
Antes de anexar uma credencial, confirme destino e política. Uma URL externa para imagens ou analytics não deve receber automaticamente o token da API.
7. Exemplo de autenticação
export const authInterceptor: HttpInterceptorFn = (request, next) => {
const session = inject(SessionService);
const token = session.accessToken();
if (!token || !request.url.startsWith("/api/")) {
return next(request);
}
return next(request.clone({
setHeaders: { Authorization: `Bearer ${token}` },
}));
};
O servidor ainda deve validar token, expiração, audiência e autorização.
8. Metadados com HttpContext
Algumas políticas pertencem à aplicação, mas não devem viajar como headers.
export const SKIP_GLOBAL_ERROR = new HttpContextToken<boolean>(() => false);
http.get("/api/health", {
context: new HttpContext().set(SKIP_GLOBAL_ERROR, true),
});
O interceptor lê request.context. Esse contexto não é enviado à API.
9. Observando a resposta
return next(request).pipe(
tap((event) => {
if (event.type === HttpEventType.Response) {
console.log(event.status, request.url);
}
}),
);
O fluxo contém diferentes HttpEvent. Verifique o tipo antes de tratar o evento como
resposta final.
10. Telemetria com finalize
export const telemetryInterceptor: HttpInterceptorFn = (request, next) => {
const startedAt = performance.now();
return next(request).pipe(
finalize(() => {
const duration = performance.now() - startedAt;
console.info(request.method, request.url, duration);
}),
);
};
finalize executa após sucesso, erro ou cancelamento. Não registre corpo, tokens ou
dados pessoais indiscriminadamente.
11. Classificação de falhas
| Situação | Indício | Ação comum |
|---|---|---|
| rede/CORS | status 0 | verificar conexão e configuração |
| timeout | erro com causa de timeout | permitir nova tentativa consciente |
| não autenticado | 401 | renovar sessão ou entrar novamente |
| sem permissão | 403 | explicar limite, não insistir |
| não encontrado | 404 | contextualizar o recurso |
| conflito | 409 | atualizar dados ou resolver versão |
| validação | 400/422 | associar problemas aos campos |
| servidor | 5xx | mensagem segura e correlação |
Não trate todos os 4xx ou 5xx com o mesmo texto.
12. Mapeamento para erro de aplicação
export interface AppHttpError {
readonly kind: "network" | "auth" | "forbidden" | "not-found" | "server" | "unknown";
readonly message: string;
readonly correlationId: string | null;
readonly retryable: boolean;
}
O modelo da interface não precisa expor toda a estrutura técnica de
HttpErrorResponse.
13. Preserve o canal de erro
return next(request).pipe(
catchError((error: HttpErrorResponse) => {
telemetry.capture(sanitize(error));
return throwError(() => error);
}),
);
O interceptor registrou e devolveu o erro. O serviço ou componente ainda decide a mensagem e o estado da operação.
14. Quando recuperar no interceptor
Recuperar globalmente só é adequado quando existe uma resposta correta para todas as
chamadas afetadas, como usar um cache válido. Transformar qualquer falha em [] faz
a interface confundir erro com vazio.
15. Retry não é “tentar até funcionar”
Retry repete a operação. Ele pode ajudar em falhas transitórias, mas aumenta carga e pode duplicar mutações.
const canRetry = request.method === "GET";
return next(request).pipe(
retry({ count: canRetry ? 2 : 0, delay: 500 }),
);
Na prática, examine o tipo de erro, aplique atraso progressivo, limite tentativas e considere jitter. POST só deve ser repetido automaticamente com uma estratégia de idempotência acordada com o servidor.
16. Timeout
http.get("/api/tasks", { timeout: 5_000 });
Timeout limita espera do cliente, mas não prova que o servidor desfez uma mutação. A operação pode ter chegado ao back-end antes de a resposta expirar.
17. Renovação de sessão
Várias requisições podem receber 401 simultaneamente. Um fluxo de renovação precisa:
- permitir uma renovação por vez;
- enfileirar ou rejeitar requisições concorrentes;
- limitar tentativas;
- evitar interceptar a própria chamada de renovação em ciclo;
- encerrar sessão quando a renovação falhar.
Não implemente recursão ilimitada dentro do interceptor.
18. Indicador global com contador
Um boolean falha quando duas requisições se sobrepõem:
requisição A inicia → loading=true
requisição B inicia → loading=true
requisição A termina → loading=false ← B ainda está ativa
Use um contador:
loading.start();
return next(request).pipe(finalize(() => loading.finish()));
O serviço expõe activeRequests > 0. Garanta que cancelamento e erro também reduzam
o contador.
19. Quem apresenta a mensagem?
interceptor → política técnica e telemetria
serviço → traduz contrato para erro de aplicação
componente → contexto, texto, foco e ação
Se todas as camadas abrirem um toast, o usuário receberá mensagens duplicadas.
20. Estados acessíveis
@switch (state().status) {
@case ("loading") {
<p role="status">Carregando tarefas...</p>
}
@case ("error") {
<section aria-labelledby="error-title">
<h2 id="error-title">Não foi possível carregar</h2>
<p role="alert">{{ state().message }}</p>
<button type="button" (click)="reload()">Tentar novamente</button>
</section>
}
}
O botão continua acessível por teclado e a mensagem não depende somente de cor.
21. status ou alert?
| Semântica | Uso |
|---|---|
role="status" |
carregamento concluído, item salvo, atualização comum |
role="alert" |
falha importante que exige atenção imediata |
| foco programático | levar a uma região que precisa ser lida e operada |
Alertas assertivos podem interromper o leitor de tela. Não use para cada pequena mudança.
22. Região viva existente
Para compatibilidade consistente, mantenha a região viva no DOM e altere seu texto:
<p role="status" aria-atomic="true">{{ announcement() }}</p>
aria-atomic="true" solicita o anúncio do conteúdo completo após a atualização.
23. Gerenciamento de foco
Depois de uma falha de envio com vários problemas, mover o foco para um resumo pode ser útil:
readonly errorSummary = viewChild<ElementRef<HTMLElement>>("errorSummary");
focusErrorSummary(): void {
this.errorSummary()?.nativeElement.focus();
}
<section #errorSummary tabindex="-1" aria-labelledby="error-title">
Não mova foco a cada atualização automática. Preserve o contexto do usuário.
24. Mensagem deve orientar
Evite “Erro 500” como único texto. Prefira:
Não foi possível carregar as tarefas. Tente novamente. Se o problema continuar, informe o código 58FA ao suporte.
O status e o stack trace podem ir para telemetria sanitizada.
25. ErrorHandler global
ErrorHandler captura erros inesperados entregues ao mecanismo global do Angular.
Use-o como última fronteira de observabilidade e recuperação segura.
@Service()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error: unknown): void {
this.telemetry.captureUnknown(error);
}
}
Não encaminhe todo 404 esperado ao ErrorHandler; trate-o no fluxo HTTP.
26. Privacidade e registros
Nunca registre indiscriminadamente:
- header
Authorization; - cookies ou tokens;
- prompts e documentos privados;
- dados pessoais;
- corpo completo de formulários;
- SQL ou stack trace na interface.
Prefira allowlist de campos, correlação e redaction.
27. Testando interceptors
Teste comportamento observável:
- header foi adicionado apenas à API correta;
- requisição original não foi mutada;
- ordem configurada foi respeitada;
- erro continuou chegando ao consumidor;
- contador voltou a zero em sucesso, falha e cancelamento;
- GET transitório respeitou o limite de retry;
- POST não foi repetido sem idempotência.
Use as ferramentas de teste do HttpClient para controlar requisições e respostas.
28. Laboratório guiado
Abra o fluxo de interceptors.
Etapa 1 — Envie uma requisição com sucesso
Observe a ordem de ida e a ordem inversa da resposta.
Etapa 2 — Inspecione os headers
Veja correlação e autorização simulada. Desative autenticação e compare.
Etapa 3 — Simule rede, 401, 403, 404 e 500
Compare classificação técnica, mensagem ao usuário e ação sugerida.
Etapa 4 — Ative retry em GET
Veja uma falha transitória ser repetida até o limite. Troque para POST e observe o bloqueio do retry automático.
Etapa 5 — Compare status e alert
O laboratório identifica qual região viva anunciaria cada estado.
Etapa 6 — Examine as fontes
Compare configuração, interceptors, mapeamento e template acessível.
29. Sobre o laboratório estático
O laboratório simula a cadeia sem enviar requisições externas. A pasta src/app
contém a configuração e os interceptors Angular equivalentes. Dados inseridos são
renderizados com textContent, não innerHTML.
30. Erros comuns
Colocar toda regra no interceptor
Ele se torna global, acoplado e difícil de entender.
Engolir o erro
Devolver lista vazia impede a tela de diferenciar falha de ausência de dados.
Anexar token em URL externa
Credenciais podem vazar para destinos que não deveriam recebê-las.
Mutar requisição diretamente
Interceptors e retries dependem de operações idempotentes e clones previsíveis.
Retry em toda operação
POST pode ser executado duas vezes.
Um boolean para requisições concorrentes
O primeiro término esconde o loading enquanto outra chamada continua.
Alertar tudo
Muitas regiões assertivas interrompem e confundem tecnologias assistivas.
Usar somente cor
Estado precisa de texto, semântica e ação compreensível.
Mostrar detalhes técnicos
Stack trace e infraestrutura não ajudam o usuário e podem expor informação sensível.
31. Exercício de fixação
Implemente:
- correlation interceptor;
- telemetry interceptor com duração;
- token de contexto para pular o loading global;
- mapeador de 401, 403, 404 e 5xx;
- região de status para sucesso;
- resumo de erro focável com botão de nova tentativa.
Desenhe a ordem completa de requisição e resposta.
32. Desafio individual
Adicione uma política de retry que:
- aceite apenas GET e HEAD;
- repita apenas status 0, 502, 503 ou 504;
- limite tentativas;
- use atraso progressivo com jitter;
- permita opt-out com
HttpContextToken; - registre apenas dados sanitizados;
- comunique a tentativa ao usuário sem alertas excessivos.
33. Lista de verificação de conclusão
- Sei explicar interceptor sem confundi-lo com API.
- Conheço a ordem da cadeia.
- Clono requisições antes de alterar headers.
- Restrinjo credenciais ao destino correto.
- Preservo erros que não resolvi.
- Diferencio 401 de 403.
- Não aplico retry cego a POST.
- Uso contador para loading concorrente.
- Distingo
status,alerte foco. - Não exponho detalhes sensíveis.
34. Critérios de avaliação
| Critério | Pontos |
|---|---|
| Cadeia e configuração corretas | 20 |
| Imutabilidade e headers seguros | 15 |
| Classificação e preservação de erros | 20 |
| Retry e concorrência conscientes | 15 |
| Retorno acessível | 20 |
| Privacidade, testes e explicação | 10 |
| Total | 100 |
35. Resumo
- interceptor é middleware do
HttpClient; - funções registradas formam uma cadeia ordenada;
- requisição e resposta devem ser clonadas para alteração;
- políticas globais não substituem regras de domínio;
- erros não resolvidos continuam no canal de erro;
- retry depende de método, falha e idempotência;
- mensagens acessíveis combinam texto, semântica, foco e ação;
ErrorHandlertrata o inesperado, não substitui tratamento HTTP.
36. Fontes oficiais
- Angular: interceptors
- Angular: fazendo requisições e tratando erros
- Angular: HttpErrorResponse
- Angular: acessibilidade
- W3C: erros com alert e regiões vivas
Próxima aula
Na Aula 4.10, concluiremos o módulo com testes de componentes e serviços, compilação de produção, documentação e apresentação do painel Angular.