【从零开始的rust web开发之路 二】axum中间件和共享状态使用

系列文章目录

第一章 axum学习使用

第二章 axum中间件使用

文章目录


前言

上篇文件讲了路由和参数相应相关的。axum还有个关键的地方是中间件的使用,这篇文件就来说说。

一、中间件是什么

这个概念跟gin框架的中间件概念一样,类似于springboot项目当中的请求过滤器,在请求过来的时候链式执行一些操作。例如鉴权,日志收集,接口幂等等

二、中间件使用

常用中间件

看看官方提供的中间件

TraceLayer用于高级跟踪/日志记录的跟踪层。

CorsLayer 用于处理 CORS。

CompressionLayer用于自动压缩的压缩层 反应。

RequestIdLayer 和 PropagateRequestIdLayer 设置和传播请求 IDS。

CompressionLayer超时的超时层。注意这一点 需要使用 HandleErrorLayer 将超时转换为响应。

我只看了前两个,这里只拿前两个举例。

使用中间件

使用中间件有两种方法,

一种是通过调用route的layer方法

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

请求将按照上图顺序执行,每一个都可以提前返回。比如在layer_three进行鉴权操作,没有权限直接就返回,不在调用下一层

还有一种方法是使用tower::ServiceBuilder构建器,例如

rust 复制代码
use tower::ServiceBuilder;
use axum::{routing::get, Router};

async fn handler() {}

let app = Router::new()
    .route("/", get(handler))
    .layer(
        ServiceBuilder::new()
            .layer(layer_one)
            .layer(layer_two)
            .layer(layer_three),
    );

遇上一种方法不同的是执行顺序,这种方式执行顺序是

layer_one 、layer_two 、layer_three、 handler、 layer_three、 layer_two、 layer_one

这种方式更符合方法调用直觉,更容易理解

使用TraceLayer中间件实现请求日志打印

首先需要额外引入一些库

rust 复制代码
tower = { version = "0.4.13" }
tower-http = { version = "0.4.3", features = ["trace"] }
tracing = "0.1.37"
tracing-subscriber = {version = "0.3.17", features = [
    "env-filter",
    "time",
    "local-time",
]}

然后

rust 复制代码
//设置日志级别并格式化时间
    use tracing_subscriber::{fmt::time::OffsetTime};
    let local_time = OffsetTime::new(
        UtcOffset::from_hms(8, 0, 0).unwrap(),
        format_description!("[year]-[month]-[day] [hour]:[minute]:[second].[subsecond digits:3]"),
    );
    let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("debug"))
        .add_directive("hyper::proto=off".parse().unwrap());
    // 输出到控制台中
    let formatting_layer = fmt::layer().with_timer(local_time).with_writer(std::io::stderr);
    Registry::default()
        .with(env_filter)
        .with(formatting_layer)
        .init();
///设置

async fn handler() {}


let app = Router::new()
    .route("/", get(handler))
    .layer(
        ServiceBuilder::new()
            .layer(TraceLayer::new_for_http()) ///使用日志中间件
    );

访问服务这时候就能看到请求日志了

自定义中间件

想要自定义中间件,这里介绍两种axum原生的方法

一种是axum::middleware::from_fn

举个例子

rust 复制代码
use axum::{
    Router,
    http::{self, Request},
    routing::get,
    response::Response,
    middleware::{self, Next},
};
///自定义中间件方法
async fn my_middleware<B>(
    request: Request<B>,
    next: Next<B>,
) -> Response {
    // 对请求做一些处理

	//......

	//调用下一个中间价
    let response = next.run(request).await;

	//......

    // 对响应做一些处理,返回响应
    response
}

//这里使用中间件
let app = Router::new()
    .route("/", get(|| async { /* ... */ }))
    .layer(middleware::from_fn(my_middleware));

还有一种方法是axum::middleware::from_extractor

举个例子

rust 复制代码
use axum::{
    extract::FromRequestParts,
    middleware::from_extractor,
    routing::{get, post},
    Router,
    http::{header, StatusCode, request::Parts},
};
use async_trait::async_trait;

// 执行认证中间件
struct RequireAuth;

#[async_trait]
impl<S> FromRequestParts<S> for RequireAuth
where
    S: Send + Sync,
{
    type Rejection = StatusCode;

    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        let auth_header = parts
            .headers
            .get(header::AUTHORIZATION)
            .and_then(|value| value.to_str().ok());

        match auth_header {
            Some(auth_header) if token_is_valid(auth_header) => {
                Ok(Self)
            }
            _ => Err(StatusCode::UNAUTHORIZED),
        }
    }
}

fn token_is_valid(token: &str) -> bool {
    // token校验
}

async fn handler() {
    // 认证之后处理
}

async fn other_handler() {
    // 认证之后处理
}

let app = Router::new()
    .route("/", get(handler))
    .route("/foo", post(other_handler))
    //给上面路由设置认证中间件
    .route_layer(from_extractor::<RequireAuth>());

类似于这个就是认证中间件的简单模板

共享状态

共享状态是用于多个请求共同访问的一些数据状态。例如db连接,redis连接等等。多个请求任务共享的数据

一共有三种方式

第一种是通过状态提取,举个例子

rust 复制代码
use axum::{
    extract::State,
    routing::get,
    Router,
};
use std::sync::Arc;

struct AppState {
    // ...
}

let shared_state = Arc::new(AppState { /* ... */ });

let app = Router::new()
    .route("/", get(handler))
    .with_state(shared_state);

async fn handler(
    State(state): State<Arc<AppState>>,
) {
    // ...
}

第二种通过扩展提取

rust 复制代码
use axum::{
    extract::Extension,
    routing::get,
    Router,
};
use std::sync::Arc;

struct AppState {
    // ...
}

let shared_state = Arc::new(AppState { /* ... */ });

let app = Router::new()
    .route("/", get(handler))
    .layer(Extension(shared_state));

async fn handler(
    Extension(state): Extension<Arc<AppState>>,
) {
    // ...
}

第三种通过闭包去获取

rust 复制代码
use axum::{
    Json,
    extract::{Extension, Path},
    routing::{get, post},
    Router,
};
use std::sync::Arc;
use serde::Deserialize;

struct AppState {
    // ...
}

let shared_state = Arc::new(AppState { /* ... */ });

let app = Router::new()
    .route(
        "/users",
        post({
            let shared_state = Arc::clone(&shared_state);
            move |body| create_user(body, shared_state)
        }),
    )
    .route(
        "/users/:id",
        get({
            let shared_state = Arc::clone(&shared_state);
            move |path| get_user(path, shared_state)
        }),
    );

async fn get_user(Path(user_id): Path<String>, state: Arc<AppState>) {
    // ...
}

async fn create_user(Json(payload): Json<CreateUserPayload>, state: Arc<AppState>) {
    // ...
}

#[derive(Deserialize)]
struct CreateUserPayload {
    // ...
}

这种方式写起来比较长,但是比较直观

第二篇先说到这儿。等后续说完orm框架时候再结合说一下

相关推荐
数字冰雹4 小时前
为城市治理装上“数字引擎”
中间件·数据可视化
不知更鸟5 小时前
Django 项目设置流程
后端·python·django
黄昏恋慕黎明6 小时前
spring MVC了解
java·后端·spring·mvc
G探险者8 小时前
为什么 VARCHAR(1000) 存不了 1000 个汉字? —— 详解主流数据库“字段长度”的底层差异
数据库·后端·mysql
百锦再8 小时前
第18章 高级特征
android·java·开发语言·后端·python·rust·django
Tony Bai9 小时前
Go 在 Web3 的统治力:2025 年架构与生态综述
开发语言·后端·架构·golang·web3
程序猿20239 小时前
项目结构深度解析:理解Spring Boot项目的标准布局和约定
java·spring boot·后端
RainbowSea9 小时前
内网穿透配置和使用
java·后端
掘金码甲哥10 小时前
网关上的限流器
后端
q***062910 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang