宣布 tower-http
2021年5月27日
今天我很高兴宣布 tower-http,它是一个 HTTP 特定中间件和实用程序的集合,使用 Tower 的 Service
trait 构建。
Tower 本身包含的中间件都是协议无关的。例如,它的 timeout 中间件 与任何 Service
实现兼容,无论它使用哪种协议。这很棒,因为它意味着中间件更具可重用性,但也意味着您无法使用特定于协议的功能。 在 HTTP 的情况下,这意味着 Tower 中的任何中间件都不知道状态码、标头或其他 HTTP 特定功能。
另一方面,tower-http 包含特定于 HTTP 的中间件。它使用 http 和 http-body crates,这意味着它与任何也使用这些的 crate 兼容,例如 hyper、tonic 和 warp。
tower-http 的目标是提供丰富的中间件集合,用于解决构建 HTTP 客户端和服务器时的常见问题。 一些亮点包括:
Trace
:轻松为您的应用程序添加高级跟踪/日志记录。 支持通过状态码以及 gRPC 特定标头确定成功或失败。 具有出色的默认值,但也支持深度自定义。Compression
和Decompression
:自动压缩或解压缩响应体。 这与使用ServeDir
服务静态文件非常搭配。FollowRedirect
:自动跟踪重定向响应。
以及一些较小的实用程序,例如设置请求和响应标头、从日志中隐藏敏感标头、授权等等。
使用 tower-http 中的组件构建一个小型服务器看起来像这样
use tower_http::{
compression::CompressionLayer,
auth::RequireAuthorizationLayer,
trace::TraceLayer,
};
use tower::{ServiceBuilder, make::Shared};
use http::{Request, Response};
use hyper::{Body, Error, server::Server};
use std::net::SocketAddr;
// Our request handler. This is where we would implement the application logic
// for responding to HTTP requests...
async fn handler(request: Request<Body>) -> Result<Response<Body>, Error> {
Ok(Response::new(Body::from("Hello, World!")))
}
#[tokio::main]
async fn main() {
// Use `tower`'s `ServiceBuilder` API to build a stack of middleware
// wrapping our request handler.
let service = ServiceBuilder::new()
// High level tracing of requests and responses.
.layer(TraceLayer::new_for_http())
// Compress responses.
.layer(CompressionLayer::new())
// Authorize requests using a token.
.layer(RequireAuthorizationLayer::bearer("tower-is-cool"))
// Wrap a `Service` in our middleware stack.
.service_fn(handler);
// And run our service using `hyper`.
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
Server::bind(&addr)
.serve(Shared::new(service))
.await
.expect("server error");
}
构建客户端看起来像这样
use tower_http::{
decompression::DecompressionLayer,
set_header::SetRequestHeaderLayer,
};
use tower::ServiceBuilder;
use hyper::Body;
use http::{Request, Response, HeaderValue, header::USER_AGENT};
let client = ServiceBuilder::new()
// Log failed requests
.layer(
TraceLayer::new_for_http()
.on_request(())
.on_response(())
.on_body_chunk(())
.on_eos(())
// leave the `on_failure` callback as the default
)
// Set a `User-Agent` header on all requests
.layer(SetRequestHeaderLayer::<_, Body>::overriding(
USER_AGENT,
HeaderValue::from_static("my-app")
))
// Decompress response bodies
.layer(DecompressionLayer::new())
// Wrap a `hyper::Client` in our middleware stack
.service(hyper::Client::new());
我们在文档上投入了大量精力,并确保所有内容都有易于理解的示例。 我们还构建了两个示例应用程序,一个使用 warp,另一个使用 tonic,向您展示如何将所有内容组合在一起。
非常欢迎贡献,如果您有任何问题,可以在 Tokio Discord 服务器中找到我们。
— David Pedersen (@davidpdrsn)