Actix-Web中间件开发

一、概述

Actix-Web中间件是用于在HTTP请求处理流程中插入自定义逻辑的组件,支持日志记录、身份验证、性能监控等功能。

二、日志中间件

修改Cargo.toml,添加futures-util 这个依赖

[dependencies]
futures-util = { version = "0.3", default-features = false, features = ["std"] }

 修改主代码 src/main.rs

use actix_web::{
    dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use actix_web::{get,App, HttpServer,Responder,HttpResponse};
use futures_util::future::LocalBoxFuture;
use std::future::{ready, Ready};
use std::time::Instant;
 
pub struct Logger;
 
impl<S, B> Transform<S, ServiceRequest> for Logger
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 = LoggerMiddleware<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;
 
    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(LoggerMiddleware { service }))
    }
}
 
pub struct LoggerMiddleware<S> {
    service: S,
}
 
impl<S, B> Service<ServiceRequest> for LoggerMiddleware<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 {
        let start = Instant::now();
        let method = req.method().to_string();
        let path = req.path().to_string();
        
        let fut = self.service.call(req);
 
        Box::pin(async move {
            let res = fut.await?;
            let elapsed = start.elapsed();
            
            println!(
                "📊 {} {} - {} - {:?}",
                method,
                path,
                res.status(),
                elapsed
            );
            
            Ok(res)
        })
    }
}

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, Actix-Web!")
}

// 使用中间件
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");
    HttpServer::new(|| {
        App::new()
            .wrap(Logger)  // 应用日志中间件
            .service(hello)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

重新运行,访问:http://127.0.0.1:8080/

页面显示:Hello, Actix-Web!

控制台输出

📊 GET / - 200 OK - 21.7µs
📊 GET /favicon.ico - 404 Not Found - 15.6µs

这里可以看到,已经执行了Box::pin里面的代码。

三、认证中间件

针对请求头Authorization做token验证

修改主代码 src/main.rs

use actix_web::{
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
    error::ErrorUnauthorized,
    get, App, Error, HttpResponse, HttpServer, Responder,
};
use futures_util::future::LocalBoxFuture;
use std::future::{ready, Ready};

pub struct AuthMiddleware;

impl<S, B> Transform<S, ServiceRequest> for AuthMiddleware
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 = AuthMiddlewareService<S>;
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ready(Ok(AuthMiddlewareService { service }))
    }
}

pub struct AuthMiddlewareService<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AuthMiddlewareService<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>>;

    actix_web::dev::forward_ready!(service);

    fn call(&self, req: ServiceRequest) -> Self::Future {
        let auth_header = req.headers().get("Authorization");

        if let Some(auth) = auth_header {
            if let Ok(auth_str) = auth.to_str() {
                if auth_str.starts_with("Bearer ") {
                    let token = &auth_str[7..];
                    if token == "valid-token" {
                        let fut = self.service.call(req);
                        return Box::pin(async move {
                            fut.await
                        });
                    }
                }
            }
        }

        Box::pin(async move {
            Err(ErrorUnauthorized("Invalid or missing token"))
        })
    }
}

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, Actix-Web!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");

    HttpServer::new(|| {
        App::new()
            .wrap(AuthMiddleware) // ✅ 应用中间件
            .service(hello)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

重新运行,访问:http://127.0.0.1:8080/

页面显示:Invalid or missing token

说明,token认证不成功,被中间件拦截了。

使用postman,携带正确的token,就可以正常访问了。

image

四、CORS 配置

 Actix-Web 提供了内置的 CORS(跨域资源共享)支持,可以轻松配置跨域请求处理。

‌核心配置要点:‌

  • ‌基础配置‌:使用 actix_cors::Cors 中间件,通过 default() 方法创建默认配置
  • ‌允许的来源‌:使用 allowed_origin() 指定允许的域名,支持精确匹配和通配符
  • ‌允许的方法‌:通过 allowed_methods() 设置允许的 HTTP 方法(GET、POST 等)
  • ‌允许的头部‌:使用 allowed_headers() 配置允许的请求头
  • ‌凭据支持‌:通过 supports_credentials() 启用 Cookie 等凭据传输

 修改主代码 src/main.rs

use actix_cors::Cors;
use actix_web::{get,http, App, HttpServer,Responder,HttpResponse};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello, Actix-Web!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
    log::info!("Starting HTTP server on http://127.0.0.1:8080");
    
    HttpServer::new(|| {
        let cors = Cors::default()
            .allowed_origin("http://localhost:3000")
            .allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
            .allowed_headers(vec![http::header::AUTHORIZATION, http::header::CONTENT_TYPE])
            .max_age(3600);
        
        App::new()
            .wrap(cors)
            .service(hello)
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

重新运行,访问:http://127.0.0.1:8080/

页面显示:Hello, Actix-Web!

本文参考链接:https://blog.youkuaiyun.com/sinat_41617212/article/details/154069236

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值