Chain of Responsibility em Rust: Cadeias de Handlers e Middleware

Implementação completa do padrão Chain of Responsibility em Rust com trait objects, middleware pattern, pipelines de validação e exemplo prático de processamento de requisições HTTP.

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).