rust中间件actix_web在项目中的使用实战

 更新时间:2024年01月17日 14:31:39   作者:ZHangQL  
这篇文章主要介绍了rust中间件在项目中的使用实战,包括自定义中间件,日志中间件,Default headers,用户会话,错误处理的用法实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

actix_web 中间件

  • 自定义中间件
  • 日志中间件
  • Default headers
  • 用户会话
  • 错误处理

中间件

中间件介绍 Actix Web 的中间件系统允许我们在请求/响应处理中添加额外的行为。中间件可以与传入的请求进程挂钩,使我们能够秀姑请求以及暂停请求处理以提前返回响应。

中间件可以hook到响应处理,通常中间件涉及一下操作

  • 预处理请求
  • 后处理请求
  • 修改应用状态
  • 访问外部服务(redis、日志、会话)

中间件为每个应用程序、范围或资源注册,并以与注册相反的顺序执行。一般来说,中间件是一种实现 Service 特征和 Transform 特征的类型。每个方法都有一个默认实现,每个方法都可以立即返回结果或返回未来的对象。

自定义中间件

use std::future::{ready, Ready};
use actix_web::{
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use futures_util::future::LocalBoxFuture;
// There are two steps in middleware processing.
// 1. Middleware initialization, middleware factory gets called with
//    next service in chain as parameter.
// 2. Middleware's call method gets called with normal request.
pub struct SayHi;
// Middleware factory is `Transform` trait
// `S` - type of the next service
// `B` - type of response's body
impl<S, B> Transform<S, ServiceRequest> for SayHi
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type InitError = ();
    type Transform = SayHiMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;
    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(SayHiMiddleware { service }))
    }
}
pub struct SayHiMiddleware<S> {
    service: S,
}
impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
    forward_ready!(service);
    fn call(&self, req: ServiceRequest) -> Self::Future {
        println!("Hi from start. You requested: {}", req.path());
        let fut = self.service.call(req);
        Box::pin(async move {
            let res = fut.await?;
            println!("Hi from response");
            Ok(res)
        })
    }
}

对于简单的用例,可以使用 wrap_fn 来创建笑的,特别的中间件。

use actix_web::{dev::Service as _, web, App};
use futures_util::future::FutureExt;
#[actix_web::main]
async fn main() {
    let app = App::new()
        .wrap_fn(|req, srv| {
            println!("Hi from start. You requested: {}", req.path());
            srv.call(req).map(|res| {
                println!("Hi from response");
                res
            })
        })
        .route(
            "/index.html",
            web::get().to(|| async { "Hello, middleware!" }),
        );
}

日志中间件

日志是作为中间件实现的,通常将日志中间件注册为应用程序的第一个中间件,必须为每个应用程序注册日志中间件。

Logger 中间件使用标准日志箱记录信息,您应该为actix web 包启用记录器以表查看访问日志(env_logger或类似)。

使用指定的格式创建 Logger 中间件。默认记录器可以用默认方法创建,使用默认的格式。

%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T

例子:

use actix_web::middleware::Logger;
use env_logger::Env;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    use actix_web::{App, HttpServer};
    env_logger::init_from_env(Env::default().default_filter_or("info"));
    HttpServer::new(|| {
        App::new()
            .wrap(Logger::default())
            .wrap(Logger::new("%a %{User-Agent}i"))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

日志格式

INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646

见日志篇章

Default headers

要设置默认的响应头,我们可以使用 DefaultHeaders 中间件。如果响应头已经包含指定的头,DefaultHeaders 中间件不会设置头。

use actix_web::{http::Method, middleware, web, App, HttpResponse, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::DefaultHeaders::new().add(("X-Version", "0.2")))
            .service(
                web::resource("/test")
                    .route(web::get().to(HttpResponse::Ok))
                    .route(web::method(Method::HEAD).to(HttpResponse::MethodNotAllowed)),
            )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

用户会话

Actix Web 提供了会话管理的通用解决方案,使用 actix-session 中间件可以使用多种后段类型来存储会话数据。默认实现cookie会话后段,也可以添加其他后段实现。

CookieSession 使用 cookie 作为会话存储,CookieSessionBackend 创建的会话呗限制为存储少于4000字节的数据,因为 payload 必须适合单个 cookie,如果会话长度超过 4000 字节,会产生服务器内部错误。

可能具有已签名或私有的安全策略,每个都有一个各自的 CookieSession 构造器。

签名的 cookie 可以被查看,但不能被客户端修改,客户端既不能查看也不能修改私有 cookie。构造函数接受一个键作为参数,这是 cookie 会话的私钥——当这个值被改变,所有会话数据都会消失。

通常,您可以创建 SessionStorage 中间件并使用特定的后段实现(如 cookiesession)对其进行初始化,要访问会话数据,必须使用会话提取器,这个方法返回一个Session 对象,它允许我们获取或设置会话数据。

1、添加依赖

cargo add actix-session

2、例子代码

use actix_session::{Session, SessionMiddleware, storage::CookieSessionStore};
use actix_web::{web, App, Error, HttpResponse, HttpServer, cookie::Key};
async fn index(session: Session) -> Result<HttpResponse, Error> {
    // access session data
    if let Some(count) = session.get::<i32>("counter")? {
        session.insert("counter", count + 1)?;
    } else {
        session.insert("counter", 1)?;
    }
    Ok(HttpResponse::Ok().body(format!(
        "Count is {:?}!",
        session.get::<i32>("counter")?.unwrap()
    )))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(
                // create cookie based session middleware
                SessionMiddleware::builder(CookieSessionStore::default(), Key::from(&[0; 64]))
                    .cookie_secure(false)
                    .build()
            )
            .service(web::resource("/").to(index))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

错误处理

ErrorHandlers 中间件允许我们为响应提供自定义处理程序,您可以使用 ErrorHandlers::handler 方法为特定状态码注册自定义错误处理程序。您可以修改现有的响应或创建一个全新的响应。错误处理程序可以立即返回响应,也可以返回解析为响应的future。

use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{
    dev,
    http::{header, StatusCode},
    web, App, HttpResponse, HttpServer, Result,
};
fn add_error_header<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
    res.response_mut().headers_mut().insert(
        header::CONTENT_TYPE,
        header::HeaderValue::from_static("Error"),
    );
    Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(
                ErrorHandlers::new()
                    .handler(StatusCode::INTERNAL_SERVER_ERROR, add_error_header),
            )
            .service(web::resource("/").route(web::get().to(HttpResponse::InternalServerError)))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}
use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::{
    dev,
    http::{header},
};
fn add_error_header<B>(mut res: dev::ServiceResponse<B>) -> Result<ErrorHandlerResponse<B>> {
    res.response_mut().headers_mut().insert(
        header::CONTENT_TYPE,
        header::HeaderValue::from_static("Error"),
    );
    Ok(ErrorHandlerResponse::Response(res.map_into_left_body()))
}
pub async fn web() {
    HttpServer::new(|| {
        App::new()
            .wrap(
                ErrorHandlers::new()
                    .handler(StatusCode::NOT_FOUND, add_error_header),
            )
            .service(web::resource("/found").route(web::get().to(HttpResponse::NotFound)))
    })
    .bind(("127.0.0.1", 18080))
    .unwrap()
    .run()
    .await
    .unwrap();
}
$ curl -v   --location --request GET  'http://127.0.0.1:18080/a/found'
Note: Unnecessary use of -X or --request, GET is already inferred.
*   Trying 127.0.0.1:18080...
* Connected to 127.0.0.1 (127.0.0.1) port 18080 (#0)
> GET /a/found HTTP/1.1
> Host: 127.0.0.1:18080
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< content-length: 0
< content-type: Error
< date: Fri, 10 Nov 2023 10:01:50 GMT
< 
* Connection #0 to host 127.0.0.1 left intact

以上就是rust 中间件在项目中的使用实战的详细内容,更多关于rust 中间件的资料请关注脚本之家其它相关文章!

相关文章

  • 详解Rust中#[derive]属性怎么使用

    详解Rust中#[derive]属性怎么使用

    在 Rust 中,#[derive] 是一个属性,用于自动为类型生成常见的实现,下面就跟随小编一起来学习一下Rust中derive属性的具体使用吧
    2024-11-11
  • Rust中引用的具体使用

    Rust中引用的具体使用

    在Rust语言中,引用机制是其所有权系统的重要组成部分,ust提供了两种类型的引用,不可变引用和可变引用,本文就来详细的介绍一下这两种的用法,感兴趣的可以了解一下
    2024-03-03
  • Rust循环控制结构用法详解

    Rust循环控制结构用法详解

    Rust提供了多种形式的循环结构,每种都适用于不同的场景,在Rust中,循环有三种主要的形式:loop、while和for,本文将介绍Rust中的这三种循环,并通过实例展示它们的用法和灵活性,感兴趣的朋友一起看看吧
    2024-02-02
  • Rust生命周期之验证引用有效性与防止悬垂引用方式

    Rust生命周期之验证引用有效性与防止悬垂引用方式

    本文介绍了Rust中生命周期注解的应用,包括防止悬垂引用、在函数中使用泛型生命周期、生命周期省略规则、在结构体中使用生命周期、静态生命周期以及如何将生命周期与泛型和特质约束结合,通过这些机制,Rust在编译时就能捕获内存安全问题
    2025-02-02
  • Rust中FFI编程知识点整理总结(推荐)

    Rust中FFI编程知识点整理总结(推荐)

    这篇文章主要介绍了Rust中FFI编程知识点整理总结,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-09-09
  • 浅谈Rust中声明可见性

    浅谈Rust中声明可见性

    在Rust编程语言中,声明可见性是一个核心概念,本文主要介绍了Rust中声明可见性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • 关于Rust命令行参数解析以minigrep为例

    关于Rust命令行参数解析以minigrep为例

    本文介绍了如何使用Rust的std::env::args函数来解析命令行参数,并展示了如何将这些参数存储在变量中,随后,提到了处理文件和搜索逻辑的步骤,包括读取文件内容、搜索匹配项和输出搜索结果,最后,总结了Rust标准库在命令行参数处理中的便捷性和社区资源的支持
    2025-02-02
  • rust 中生成与使用protobuf的方法

    rust 中生成与使用protobuf的方法

    这篇文章主要介绍了rust中protobuf生成与使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-05-05
  • 一文弄懂rust生命周期

    一文弄懂rust生命周期

    生命周期是Rust语言中的一个概念,用于决内存安全问题,本文主要介绍了一文弄懂rust生命周期,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • 详解Rust 生命周期符号使用的方法和规律

    详解Rust 生命周期符号使用的方法和规律

    生命周期是 Rust 中处理引用和所有权的关键概念,通过正确使用生命周期符号和遵循相关规律,你可以编写出安全、高效的 Rust 代码,这篇文章主要介绍了Rust 生命周期符号使用的方法和规律,需要的朋友可以参考下
    2024-03-03

最新评论