5 个版本

0.3.4 2024年6月26日
0.3.3 2024年6月26日
0.3.1 2023年10月15日
0.2.2 2023年7月25日
0.2.1 2023年7月25日

463Rust 模式

Download history 224/week @ 2024-06-23 33/week @ 2024-06-30 59/week @ 2024-07-28

每月下载 59

MIT/Apache

84KB
1.5K SLoC

sod-actix-web

该包提供了通过 sod::Service 抽象,在 actix_web 上通过 Handler 实现的抽象。

处理器

ServiceHandler 充当一个 actix_web Handler,将请求调度到底层的 sod::AsyncServicesod::Service 实现。

服务 I/O

底层 AsyncService 的输入与 actix_web 中的本地 FromRequest 特性直接兼容。因此,一个 FromRequest 实现的元组可以作为 AsyncService 的输入处理。

底层 AsyncService 的输出必须实现来自 actix_web 的本地 Responder 特性。这意味着服务输出的所有类型都应与来自 actix_web 的所有输出类型兼容。这应包括简单的 String 或完整的 actix_web::HttpResponse

问候服务器示例

以下示例与默认的 actix_web 欢迎示例类似,但它使用此库提供的服务抽象

use actix_web::{web, App, HttpServer};
use sod::Service;
use sod_actix_web::ServiceHandler;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    struct GreetService;
    impl Service for GreetService {
        type Input = web::Path<String>;
        type Output = String;
        type Error = std::convert::Infallible;
        fn process(&self, name: web::Path<String>) -> Result<Self::Output, Self::Error> {
            Ok(format!("Hello {name}!"))
        }
    }

    HttpServer::new(|| {
        App::new().service(
            web::resource("/greet/{name}").route(web::get().to(ServiceHandler::new(GreetService.into_async()))),
        )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

数学服务器示例

以下示例稍微复杂一些,展示了如何使用 AsyncService 和输入的元组

use std::{io::Error, io::ErrorKind};
use actix_web::{web, App, HttpServer};
use serde_derive::Deserialize;
use sod::{async_trait, AsyncService};
use sod_actix_web::ServiceHandler;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    #[derive(Debug, Deserialize)]
    pub struct MathParams {
        a: i64,
        b: i64,
    }

    struct MathService;
    #[async_trait]
    impl AsyncService for MathService {
        type Input = (web::Path<String>, web::Query<MathParams>);
        type Output = String;
        type Error = Error;
        async fn process(
            &self,
            (func, params): (web::Path<String>, web::Query<MathParams>),
        ) -> Result<Self::Output, Self::Error> {
            let value = match func.as_str() {
                "add" => params.a + params.b,
                "sub" => params.a - params.b,
                "mul" => params.a * params.b,
                "div" => params.a / params.b,
                _ => return Err(Error::new(ErrorKind::Other, "invalid func")),
            };
            Ok(format!("{value}"))
        }
    }

    HttpServer::new(|| {
        App::new().service(
            web::resource("/math/{func}").route(web::get().to(ServiceHandler::new(MathService))),
        )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

websockets

一个 actix_web Handler,它实例化 MutService 实现来处理单个会话。

会话工厂

WsSessionFactory 是此模块的入口点。它用于在客户端建立连接时创建会话服务,并充当一个 actix_web Handler

WsSessionFactory 封装了用户需要定义的两个函数。

  • F: Fn(&HttpRequest) -> Result<S, Error> + 'static - 用于生成会话服务的工厂,可以生成 Ok(Service)、Ok(MutService) 或 Err(Error)。
  • E: Fn(&mut S, S::Error)-> Result<(), S::Error>+ Unpin + 'static - 错误处理器,用作回调来处理服务实现返回的错误。

Actix 配线

web::resource("/echo").route(web::get().to(WsSessionFactory::new(
  |_req| Ok(EchoService),
  |_service, err| println!("ERROR: {err}"),
))),

会话服务

会话服务工厂生成的底层会话 MutService 实现必须接受 WsSessionEvent 作为输入,并生成 Option<WsMessage> 作为输出。

  • WsSessionEvent 输入会通知会话会话生命周期事件和接收到的消息。
  • Option<WsMessage> 输出可以选择向会话发送响应负载。

错误处理

WsSessionFactory 需要用户提供一个 Fn(&mut S, S::Error) -> Result<(), S::Error> 错误处理器函数,其中 S: MutServiceS: Service。由于 actix 在幕后使用异步线程池来处理 WebSocket 请求,ServiceErr 结果无法在底层的 StreamHandler 外部传播。

用户完全可以通过错误处理器来处理服务返回的错误,而不是假设用户如何处理错误。当错误处理器返回 Ok(()) 时,不会对底层的会话采取任何行动。当错误处理器返回 Err 时,会话将被关闭。

常见的错误处理器实现是记录错误并关闭会话

|_, err| {
    log::error!("Session Error: {err}");
    Err(err)
}

WsSendService

要在输入事件处理之外向会话生成消息,请使用初始 WsSessionEvent::Started 事件提供的 WsSendService。您可以将 WsSendService 的所有权用于异步向服务外部生成消息,这将在会话关闭/关闭后返回一个 SendError

Ping/Pong

本框架会自动发送Pong响应,因此您可以在Ping/Pong响应的目的下忽略Ping请求。

回声示例

use std::convert::Infallible;
use actix_web::{web, App, HttpServer};
use sod::MutService;
use sod_actix_web::ws::{WsMessage, WsSessionEvent, WsSessionFactory};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    struct EchoService;
    impl MutService<WsSessionEvent> for EchoService {
        type Output = Option<WsMessage>;
        type Error = Infallible;
        fn process(&mut self, event: WsSessionEvent) -> Result<Self::Output, Self::Error> {
            Ok(match event {
                WsSessionEvent::Started(_) => {
                    Some(WsMessage::Text("Welcome to EchoServer!".to_owned()))
                }
                WsSessionEvent::Message(message) => match message {
                    WsMessage::Binary(data) => Some(WsMessage::Binary(data)),
                    WsMessage::Text(text) => Some(WsMessage::Text(text)),
                    _ => None, // note: pongs are sent automatically
                },
                _ => None,
            })
        }
    }

    HttpServer::new(|| {
        App::new().service(
            web::resource("/echo").route(web::get().to(WsSessionFactory::new(
                |_| Ok(EchoService),
                |_, err| {
                    println!("ERROR: {err}");
                    Err(err)
                },
            ))),
        )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

依赖项

约15–26MB
约460K SLoC