本文主要讨论axum
的中间件,这也是axum
与其他rust web框架极大的不同点,其他框架都定义了自己的接口,而axum
直接使用tower
的Servvice
和layer
的概念, 而tower
已经存在一大堆通用的中间件了,所以axum
也就自带了一大堆开箱即用的中间件。
往期文章:
- youerning.top/post/axum/q...
- youerning.top/post/axum/q...
- youerning.top/post/axum/q...
- youerning.top/post/axum/q...
本节的Cargo.toml
文件依赖如下
toml
[dependencies]
axum = { version="0.6.20", features=["default", "headers"] }
axum-extra = { version = "0.8" }
tokio = { version = "1.0", features = ["full"] }
reqwest = { version="0.11.22", features=["json", "multipart"]}
serde = { version = "1.0", features = ["derive"] }
tower = { version = "0.4", features = ["util"] }
tower-http = { version = "0.4.0", features = ["full"] }
serde_json = "1.0.107"
askama = "0.12"
sqlx = { version = "0.7", features = [ "runtime-tokio", "postgres"] }
matchit = "0.7.3"
rand = "0.8.5"
http = "1.0.0"
hyper = "0.14.27"
快速入门
rust
// https://youerning.top/post/axum/quickstart-5
use std::time::Duration;
use axum::http::HeaderName;
use axum::{
body::Body,
routing::get,
Router,
http::Request,
};
use tokio::time::sleep;
use tower_http::trace::TraceLayer;
use tower_http::compression::CompressionLayer;
use tower_http::timeout::TimeoutLayer;
use tower_http::cors::{CorsLayer, Any};
use tower_http::request_id::{MakeRequestUuid, SetRequestIdLayer};
use tower::ServiceBuilder;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(index_handler))
.layer(
// 官方推荐在ServiceBuilder上一次性载入
ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(CompressionLayer::new())
// 超时时间是200ms
.layer(TimeoutLayer::new(Duration::new(0, 200000)))
// 需要设置一个请求头的键名,一般叫x-request-id
.layer(SetRequestIdLayer::new(HeaderName::from_static("x-request-id"), MakeRequestUuid))
// 默认情况下不放行,所以需要根据自己需求设置必要的允许规则。
.layer(CorsLayer::new().allow_methods(axum::http::Method::GET).allow_origin(Any))
);
let addr = "0.0.0.0:8080";
axum::Server::bind(&addr.parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn index_handler(req: Request<Body>) -> String {
if rand::random() {
sleep(Duration::new(0, 300000)).await;
}
if let Some(req_id) = req.headers().get("x-request-id") {
// CompressionLayer只有当长度大于32时才会压缩
format!("request[{:?}] {} with method {} ; make the body length longer than 32", req_id ,req.uri(), req.method())
} else {
format!("request[none] {} with method {} ; make the body length longer than 32", req.uri(), req.method())
}
}
上面使用了五个比较常用的中间件。
- TraceLayer trace中间件, 用于追踪各种事件,在不发送给远端的情况的话,跟日志差不多
- CompressionLayer 压缩中间件,默认情况下,只有内容长度大于32的时候才会进行压缩
- TimeoutLayer 超时中间件 如果服务端响应超时就返回408状态码
- SetRequestIdLayer 为服务端设置请求ID,如果还需要为客户端生成对应的请求ID,那么需要PropagateRequestIdLayer
- CorsLayer 设置CORS请求头,做过前后端分离的项目应该不陌生
除此之外,tower_http
还有超级多的中间件可以使用,有兴趣的可以查看: docs.rs/tower-http/...
顺序
tower_http
的中间件嵌套其实就像洋葱一层包一层
rust
use axum::{routing::get, Router};
async fn handler() {}
let app = Router::new()
.route("/", get(handler))
.layer(layer_one)
.layer(layer_two)
.layer(layer_three);
上面的代码的请求顺序如下
rust
requests
|
v
+----- layer_three -----+
| +---- layer_two ----+ |
| | +-- layer_one --+ | |
| | | | | |
| | | handler | | |
| | | | | |
| | +-- layer_one --+ | |
| +---- layer_two ----+ |
+----- layer_three -----+
|
v
responses
小结
这一节主要了解了一下axum
或者说tower
的一些常见中间件的使用,如果tower
的中间件列表没有自己需要的,可以自己写,虽然会写的不会太难,但是对于没有接触过tower
的人来说还是有点难以理解,所以作为快速入门,这里就省略了自己写中间件的内容了, 当然了,也是觉得解释tower
的Service
概念需要花很多时间,以后有机会在写相关文章吧。