O que é Tower e para que serve?
\n\nTower é uma biblioteca fundamental do ecossistema Rust que fornece abstrações para construir serviços de rede modulares, compostos e assíncronos e middleware. Ela não é um framework HTTP pronto (como Actix-web ou Axum), mas sim um "material de construção" para eles. Axum, por exemplo, é construído sobre Tower.
\n\nA ideia principal do Tower é a trait Service, que representa uma função assíncrona que transforma uma requisição (Request) em uma resposta (Response). Middleware (ou "Layer") no Tower é uma forma de encapsular um serviço dentro de outro, adicionando comportamento extra (logging, autenticação, timeouts, balanceamento de carga, etc.) sem modificar o código original do serviço.
Para que serve?
\n- \n
- Modularidade: Você pode dividir a lógica complexa de processamento de requisições em componentes pequenos, independentes e reutilizáveis (serviços e middleware). \n
- Composição: Middleware pode ser facilmente combinado, "empilhando" uns sobre os outros como camadas (layers). \n
- Assincronia: Tower é projetada para funcionar com Rust assíncrono (tokio, async-std) e suporta futures. \n
- Testabilidade: Cada componente pode ser testado isoladamente. \n
Instalação do Tower
\n\nTower é uma biblioteca Rust, portanto é instalada através do gerenciador de pacotes Cargo. Adicione a dependência ao seu arquivo Cargo.toml:
[dependencies]\ntower = { version = "0.4", features = ["full"] } # "full" inclui todos os recursos\ntokio = { version = "1", features = ["full"] } # Para o runtime assíncrono\n\n\nDepois disso, execute o comando no terminal:
\ncargo build\n\nA biblioteca será baixada e compilada junto com seu projeto.
\n\nInício rápido: Seu primeiro serviço e middleware
\n\nVamos criar um serviço simples que recebe uma string e a retorna em maiúsculas. Em seguida, vamos encapsulá-lo em um middleware que registra (log) cada requisição.
\n\nuse std::convert::Infallible;\nuse std::task::{Context, Poll};\nuse tower::{Service, Layer, ServiceBuilder};\nuse tower::layer::util::Identity;\nuse std::future::{Ready, ready};\n\n// 1. Definindo nosso serviço\n#[derive(Clone)]\nstruct MyService;\n\nimpl<Request> Service<Request> for MyService {\n type Response = String;\n type Error = Infallible;\n type Future = Ready<Result<Self::Response, Self::Error>>;\n\n fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n Poll::Ready(Ok(()))\n }\n\n fn call(&mut self, req: Request) -> Self::Future {\n // Simplesmente retorna uma string\n ready(Ok(format!("Olá do serviço! Requisição: {:?}", std::any::type_name::<Request>())))\n }\n}\n\n// 2. Definindo middleware para logging\n#[derive(Clone)]\nstruct LogMiddleware;\n\nimpl<S> Layer<S> for LogMiddleware {\n type Service = LogService<S>;\n\n fn layer(&self, inner: S) -> Self::Service {\n LogService { inner }\n }\n}\n\n#[derive(Clone)]\nstruct LogService<S> {\n inner: S,\n}\n\nimpl<S, Request> Service<Request> for LogService<S>\nwhere\n S: Service<Request>,\n S::Future: std::marker::Send,\n Request: std::fmt::Debug,\n{\n type Response = S::Response;\n type Error = S::Error;\n type Future = S::Future;\n\n fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {\n self.inner.poll_ready(cx)\n }\n\n fn call(&mut self, req: Request) -> Self::Future {\n printl