O Chain of Responsibility (Cadeia de Responsabilidade) é um padrão comportamental que permite passar uma solicitação por uma cadeia de handlers. Cada handler decide se processa a solicitação ou a passa adiante para o próximo handler na cadeia. Este padrão é a base do conceito de middleware, amplamente usado em frameworks web como Actix-web, Axum e Tower.
Em Rust, o padrão se implementa de forma elegante usando trait objects para handlers dinâmicos, closures para middleware leve, ou enums quando os handlers são conhecidos em tempo de compilação. A composição de handlers forma pipelines poderosos e testáveis.
Problema
Considere um sistema de processamento de requisições HTTP. Cada requisição precisa passar por várias etapas: autenticação, autorização, validação de rate limit, logging, compressão. Sem o Chain of Responsibility:
// ANTI-PADRÃO: lógica monolítica com ifs aninhados
fn processar_requisicao(req: &Requisicao) -> Resposta {
// Logging
println!("Requisição recebida: {}", req.caminho);
// Rate limit
if req.ip_bloqueado() {
return Resposta::erro(429, "Rate limit excedido");
}
// Autenticação
if !req.token_valido() {
return Resposta::erro(401, "Não autenticado");
}
// Autorização
if !req.tem_permissao() {
return Resposta::erro(403, "Sem permissão");
}
// Finalmente, lógica de negócio
processar_logica_negocio(req)
}
Este código é difícil de testar, reusar e estender. Adicionar um novo middleware requer modificar a função monolítica.
Solução em Rust
Abordagem 1: Chain com Trait Objects
use std::collections::HashMap;
use std::fmt;
/// Requisição HTTP simplificada
#[derive(Debug, Clone)]
struct Requisicao {
metodo: String,
caminho: String,
headers: HashMap<String, String>,
corpo: Option<String>,
ip: String,
}
impl Requisicao {
fn nova(metodo: &str, caminho: &str) -> Self {
Requisicao {
metodo: metodo.to_string(),
caminho: caminho.to_string(),
headers: HashMap::new(),
corpo: None,
ip: "127.0.0.1".to_string(),
}
}
fn com_header(mut self, chave: &str, valor: &str) -> Self {
self.headers.insert(chave.to_string(), valor.to_string());
self
}
fn com_corpo(mut self, corpo: &str) -> Self {
self.corpo = Some(corpo.to_string());
self
}
fn com_ip(mut self, ip: &str) -> Self {
self.ip = ip.to_string();
self
}
}
/// Resposta HTTP simplificada
#[derive(Debug, Clone)]
struct Resposta {
status: u16,
corpo: String,
headers: HashMap<String, String>,
}
impl Resposta {
fn sucesso(corpo: &str) -> Self {
Resposta {
status: 200,
corpo: corpo.to_string(),
headers: HashMap::new(),
}
}
fn erro(status: u16, mensagem: &str) -> Self {
Resposta {
status,
corpo: mensagem.to_string(),
headers: HashMap::new(),
}
}
}
impl fmt::Display for Resposta {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HTTP {} - {}", self.status, self.corpo)
}
}
/// Trait para handlers na cadeia
trait Handler: fmt::Debug {
/// Processa a requisição. Pode:
/// - Retornar uma resposta (interrompe a cadeia)
/// - Retornar None (passa para o próximo handler)
fn processar(&self, req: &Requisicao) -> Option<Resposta>;
/// Nome do handler para logging
fn nome(&self) -> &str;
}
/// Cadeia de handlers
struct CadeiaHandlers {
handlers: Vec<Box<dyn Handler>>,
handler_final: Box<dyn Fn(&Requisicao) -> Resposta>,
}
impl CadeiaHandlers {
fn nova<F>(handler_final: F) -> Self
where
F: Fn(&Requisicao) -> Resposta + 'static,
{
CadeiaHandlers {
handlers: Vec::new(),
handler_final: Box::new(handler_final),
}
}
fn adicionar(&mut self, handler: impl Handler + 'static) {
self.handlers.push(Box::new(handler));
}
/// Executa a cadeia completa
fn executar(&self, req: &Requisicao) -> Resposta {
for handler in &self.handlers {
println!("[CADEIA] Executando: {}", handler.nome());
if let Some(resposta) = handler.processar(req) {
println!("[CADEIA] {} interrompeu a cadeia", handler.nome());
return resposta;
}
}
// Se nenhum handler interrompeu, executa o handler final
println!("[CADEIA] Todos os handlers passaram — executando handler final");
(self.handler_final)(req)
}
}
// ============================================================
// Handlers concretos
// ============================================================
/// Handler de logging — nunca interrompe a cadeia
#[derive(Debug)]
struct HandlerLogging;
impl Handler for HandlerLogging {
fn processar(&self, req: &Requisicao) -> Option<Resposta> {
println!("[LOG] {} {} de {}", req.metodo, req.caminho, req.ip);
None // Sempre passa adiante
}
fn nome(&self) -> &str {
"Logging"
}
}
/// Handler de rate limit
#[derive(Debug)]
struct HandlerRateLimit {
ips_bloqueados: Vec<String>,
limite_por_minuto: u32,
}
impl HandlerRateLimit {
fn novo(limite: u32) -> Self {
HandlerRateLimit {
ips_bloqueados: vec!["192.168.1.100".to_string()],
limite_por_minuto: limite,
}
}
}
impl Handler for HandlerRateLimit {
fn processar(&self, req: &Requisicao) -> Option<Resposta> {
if self.ips_bloqueados.contains(&req.ip) {
Some(Resposta::erro(
429,
&format!("Rate limit excedido ({}/min)", self.limite_por_minuto),
))
} else {
None
}
}
fn nome(&self) -> &str {
"RateLimit"
}
}
/// Handler de autenticação
#[derive(Debug)]
struct HandlerAutenticacao {
tokens_validos: Vec<String>,
}
impl HandlerAutenticacao {
fn novo(tokens: Vec<String>) -> Self {
HandlerAutenticacao { tokens_validos: tokens }
}
}
impl Handler for HandlerAutenticacao {
fn processar(&self, req: &Requisicao) -> Option<Resposta> {
match req.headers.get("Authorization") {
None => Some(Resposta::erro(401, "Token de autenticação ausente")),
Some(token) => {
let token_limpo = token.trim_start_matches("Bearer ");
if self.tokens_validos.contains(&token_limpo.to_string()) {
None // Token válido — continua
} else {
Some(Resposta::erro(401, "Token inválido"))
}
}
}
}
fn nome(&self) -> &str {
"Autenticacao"
}
}
/// Handler de autorização por caminho
#[derive(Debug)]
struct HandlerAutorizacao {
caminhos_admin: Vec<String>,
}
impl Handler for HandlerAutorizacao {
fn processar(&self, req: &Requisicao) -> Option<Resposta> {
let eh_admin = req.headers.get("X-Role")
.map_or(false, |r| r == "admin");
let requer_admin = self.caminhos_admin.iter()
.any(|c| req.caminho.starts_with(c));
if requer_admin && !eh_admin {
Some(Resposta::erro(403, "Acesso restrito a administradores"))
} else {
None
}
}
fn nome(&self) -> &str {
"Autorizacao"
}
}
/// Handler de validação do corpo da requisição
#[derive(Debug)]
struct HandlerValidacaoCorpo {
tamanho_maximo: usize,
}
impl Handler for HandlerValidacaoCorpo {
fn processar(&self, req: &Requisicao) -> Option<Resposta> {
if let Some(corpo) = &req.corpo {
if corpo.len() > self.tamanho_maximo {
return Some(Resposta::erro(
413,
&format!("Corpo excede {} bytes", self.tamanho_maximo),
));
}
}
None
}
fn nome(&self) -> &str {
"ValidacaoCorpo"
}
}
fn main() {
// Montando a cadeia de handlers
let mut cadeia = CadeiaHandlers::nova(|req| {
Resposta::sucesso(&format!(
"Processado: {} {}",
req.metodo, req.caminho
))
});
cadeia.adicionar(HandlerLogging);
cadeia.adicionar(HandlerRateLimit::novo(100));
cadeia.adicionar(HandlerAutenticacao::novo(vec![
"token_valido_123".to_string(),
"token_admin_456".to_string(),
]));
cadeia.adicionar(HandlerAutorizacao {
caminhos_admin: vec!["/admin".to_string()],
});
cadeia.adicionar(HandlerValidacaoCorpo { tamanho_maximo: 1024 });
println!("=== Requisição válida ===");
let req = Requisicao::nova("GET", "/api/usuarios")
.com_header("Authorization", "Bearer token_valido_123");
let resp = cadeia.executar(&req);
println!("Resposta: {}\n", resp);
println!("=== IP bloqueado ===");
let req = Requisicao::nova("GET", "/api/dados")
.com_ip("192.168.1.100");
let resp = cadeia.executar(&req);
println!("Resposta: {}\n", resp);
println!("=== Sem autenticação ===");
let req = Requisicao::nova("POST", "/api/dados");
let resp = cadeia.executar(&req);
println!("Resposta: {}\n", resp);
println!("=== Sem permissão de admin ===");
let req = Requisicao::nova("DELETE", "/admin/usuarios")
.com_header("Authorization", "Bearer token_valido_123");
let resp = cadeia.executar(&req);
println!("Resposta: {}\n", resp);
println!("=== Requisição admin válida ===");
let req = Requisicao::nova("DELETE", "/admin/usuarios")
.com_header("Authorization", "Bearer token_admin_456")
.com_header("X-Role", "admin");
let resp = cadeia.executar(&req);
println!("Resposta: {}", resp);
}
Diagrama
Chain of Responsibility — Fluxo da Requisição:
Requisição
│
▼
┌──────────┐ prosseguir ┌──────────┐ prosseguir
│ Logging │ ──────────────▶ │RateLimit │ ──────────────▶ ...
│ (sempre │ │(verifica │
│ passa) │ │ IP) │
└──────────┘ └────┬─────┘
│ bloqueado?
▼
HTTP 429
(interrompe)
... ──▶ ┌──────────┐ ──▶ ┌──────────┐ ──▶ ┌──────────┐
│ Auth │ │Autoriz. │ │Validação │
│(verifica │ │(verifica │ │ (corpo) │
│ token) │ │ permissão│ │ │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ sem token? │ sem perm? │ muito grande?
▼ ▼ ▼
HTTP 401 HTTP 403 HTTP 413
Se TODOS passam:
┌──────────────────────┐
│ Handler Final │
│ (lógica de negócio) │ ──▶ HTTP 200
└──────────────────────┘
Exemplo do Mundo Real
Pipeline de validação de dados com acumulação de erros:
/// Erro de validação com campo e mensagem
#[derive(Debug, Clone)]
struct ErroValidacao {
campo: String,
mensagem: String,
severidade: Severidade,
}
#[derive(Debug, Clone, PartialEq)]
enum Severidade {
Erro,
Aviso,
}
impl fmt::Display for ErroValidacao {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let prefix = match self.severidade {
Severidade::Erro => "ERRO",
Severidade::Aviso => "AVISO",
};
write!(f, "[{}] {}: {}", prefix, self.campo, self.mensagem)
}
}
/// Resultado de uma validação
struct ResultadoValidacao {
erros: Vec<ErroValidacao>,
}
impl ResultadoValidacao {
fn novo() -> Self {
ResultadoValidacao { erros: Vec::new() }
}
fn adicionar_erro(&mut self, campo: &str, mensagem: &str) {
self.erros.push(ErroValidacao {
campo: campo.to_string(),
mensagem: mensagem.to_string(),
severidade: Severidade::Erro,
});
}
fn adicionar_aviso(&mut self, campo: &str, mensagem: &str) {
self.erros.push(ErroValidacao {
campo: campo.to_string(),
mensagem: mensagem.to_string(),
severidade: Severidade::Aviso,
});
}
fn tem_erros(&self) -> bool {
self.erros.iter().any(|e| e.severidade == Severidade::Erro)
}
}
/// Dados de cadastro para validar
#[derive(Debug)]
struct Cadastro {
nome: String,
email: String,
senha: String,
idade: u32,
cep: String,
}
/// Trait para validadores na cadeia
trait Validador: fmt::Debug {
fn validar(&self, dados: &Cadastro, resultado: &mut ResultadoValidacao);
fn nome(&self) -> &str;
}
/// Validador de campos obrigatórios
#[derive(Debug)]
struct ValidadorCamposObrigatorios;
impl Validador for ValidadorCamposObrigatorios {
fn validar(&self, dados: &Cadastro, resultado: &mut ResultadoValidacao) {
if dados.nome.trim().is_empty() {
resultado.adicionar_erro("nome", "Campo obrigatório");
}
if dados.email.trim().is_empty() {
resultado.adicionar_erro("email", "Campo obrigatório");
}
if dados.senha.is_empty() {
resultado.adicionar_erro("senha", "Campo obrigatório");
}
}
fn nome(&self) -> &str { "CamposObrigatórios" }
}
/// Validador de formato de email
#[derive(Debug)]
struct ValidadorEmail;
impl Validador for ValidadorEmail {
fn validar(&self, dados: &Cadastro, resultado: &mut ResultadoValidacao) {
if !dados.email.is_empty() {
if !dados.email.contains('@') || !dados.email.contains('.') {
resultado.adicionar_erro("email", "Formato de email inválido");
}
}
}
fn nome(&self) -> &str { "Email" }
}
/// Validador de força da senha
#[derive(Debug)]
struct ValidadorSenha {
tamanho_minimo: usize,
}
impl Validador for ValidadorSenha {
fn validar(&self, dados: &Cadastro, resultado: &mut ResultadoValidacao) {
if !dados.senha.is_empty() {
if dados.senha.len() < self.tamanho_minimo {
resultado.adicionar_erro(
"senha",
&format!("Mínimo {} caracteres", self.tamanho_minimo),
);
}
if !dados.senha.chars().any(|c| c.is_uppercase()) {
resultado.adicionar_aviso("senha", "Recomendado incluir letra maiúscula");
}
if !dados.senha.chars().any(|c| c.is_ascii_digit()) {
resultado.adicionar_aviso("senha", "Recomendado incluir número");
}
}
}
fn nome(&self) -> &str { "Senha" }
}
/// Validador de idade
#[derive(Debug)]
struct ValidadorIdade;
impl Validador for ValidadorIdade {
fn validar(&self, dados: &Cadastro, resultado: &mut ResultadoValidacao) {
if dados.idade < 13 {
resultado.adicionar_erro("idade", "Idade mínima: 13 anos");
} else if dados.idade > 120 {
resultado.adicionar_erro("idade", "Idade inválida");
}
}
fn nome(&self) -> &str { "Idade" }
}
/// Pipeline de validação — Chain of Responsibility
struct PipelineValidacao {
validadores: Vec<Box<dyn Validador>>,
}
impl PipelineValidacao {
fn novo() -> Self {
PipelineValidacao { validadores: Vec::new() }
}
fn adicionar(mut self, validador: impl Validador + 'static) -> Self {
self.validadores.push(Box::new(validador));
self
}
fn validar(&self, dados: &Cadastro) -> ResultadoValidacao {
let mut resultado = ResultadoValidacao::novo();
for validador in &self.validadores {
println!("[PIPELINE] Executando validador: {}", validador.nome());
validador.validar(dados, &mut resultado);
}
resultado
}
}
fn main() {
// Construindo o pipeline
let pipeline = PipelineValidacao::novo()
.adicionar(ValidadorCamposObrigatorios)
.adicionar(ValidadorEmail)
.adicionar(ValidadorSenha { tamanho_minimo: 8 })
.adicionar(ValidadorIdade);
// Cadastro com problemas
let cadastro = Cadastro {
nome: "Ana".to_string(),
email: "ana_sem_arroba".to_string(),
senha: "abc".to_string(),
idade: 10,
cep: "01310100".to_string(),
};
println!("Validando cadastro: {:?}\n", cadastro);
let resultado = pipeline.validar(&cadastro);
println!("\nResultado da validação:");
for erro in &resultado.erros {
println!(" {}", erro);
}
println!("Válido: {}", !resultado.tem_erros());
}
Quando Usar
- Middleware HTTP: Logging, autenticação, compressão, rate limiting
- Pipelines de validação: Validação de formulários, dados de API, importação de dados
- Processamento de eventos: Filtros de log, processamento de mensagens
- Fluxos de aprovação: Workflows onde diferentes níveis de autoridade revisam
- Fallback chains: Tentar múltiplas estratégias até uma funcionar
Quando NÃO Usar
- Ordem irrelevante: Se os handlers podem executar em qualquer ordem e todos devem executar, use Observer
- Único handler: Se sempre há exatamente um handler para cada tipo de requisição, use Strategy ou match
- Performance crítica: Trait objects impedem inlining; para hot paths, prefira dispatch estático
- Cadeia muito longa: Cadeias com 20+ handlers se tornam difíceis de depurar
Variações em Rust
Middleware com closures (estilo Tower)
type MiddlewareFn = Box<dyn Fn(&Requisicao, &dyn Fn(&Requisicao) -> Resposta) -> Resposta>;
struct PilhaMiddleware {
middlewares: Vec<MiddlewareFn>,
handler: Box<dyn Fn(&Requisicao) -> Resposta>,
}
impl PilhaMiddleware {
fn novo(handler: impl Fn(&Requisicao) -> Resposta + 'static) -> Self {
PilhaMiddleware {
middlewares: Vec::new(),
handler: Box::new(handler),
}
}
fn usar(&mut self, middleware: impl Fn(&Requisicao, &dyn Fn(&Requisicao) -> Resposta) -> Resposta + 'static) {
self.middlewares.push(Box::new(middleware));
}
fn executar(&self, req: &Requisicao) -> Resposta {
// Compor todos os middlewares em uma única cadeia
let handler = &self.handler;
let mut proximo: Box<dyn Fn(&Requisicao) -> Resposta> = Box::new(|r| (handler)(r));
// Iterar na ordem reversa para aninhar corretamente
for mw in self.middlewares.iter().rev() {
let proximo_capturado = proximo;
let mw_ref = mw;
proximo = Box::new(move |r| mw_ref(r, &*proximo_capturado));
}
proximo(req)
}
}
Padrões Relacionados
- Decorator: Decorator envolve um objeto; Chain of Responsibility passa a requisição adiante
- Strategy: Strategy seleciona um algoritmo; Chain tenta múltiplos handlers
- Command: Comandos podem ser passados por uma cadeia de handlers
- Observer: Observer notifica todos os inscritos; Chain para no primeiro que processa
Conclusão
O Chain of Responsibility em Rust é a base do padrão de middleware usado em praticamente todo framework web do ecossistema. A combinação de trait objects para handlers dinâmicos, closures para middleware leve e o sistema de ownership para passagem segura de dados torna Rust uma linguagem excelente para construir pipelines de processamento. O padrão promove desacoplamento (cada handler conhece apenas sua responsabilidade), testabilidade (handlers são testados isoladamente) e extensibilidade (novos handlers são adicionados sem modificar os existentes).