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日 |
463 在 Rust 模式 中
每月下载 59 次
84KB
1.5K SLoC
sod-actix-web
该包提供了通过 sod::Service 抽象,在 actix_web 上通过 Handler 实现的抽象。
处理器
ServiceHandler 充当一个 actix_web Handler,将请求调度到底层的 sod::AsyncService 或 sod::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: MutService 或 S: Service。由于 actix 在幕后使用异步线程池来处理 WebSocket 请求,Service 的 Err 结果无法在底层的 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