项目地址
https://github.com/dakai-chen/boluo
介绍
boluo
是一个基于 tokio
和 hyper
开发的轻量级路由层,几乎没有额外的性能开销,拥有极快的运行速度。
特点
- 简单清晰的路由定义,支持嵌套路由、宏定义路由和路由合并。
- 提供核心 Trait
Service
和Middleware
,灵活且易于扩展。 - 提供统一的错误处理机制,简化了错误处理逻辑。
可选功能
功能名 | 描述 | 默认启用 |
---|---|---|
http1 | 启用HTTP1服务器 | 是 |
http2 | 启用HTTP2服务器 | |
multipart | 添加对multipart/form-data 格式的支持 |
|
sse | 添加对服务器发送事件的支持 | |
ws | 添加对网络套接字的支持 | |
static-file | 添加对静态文件的支持 |
快速开始
新建项目:
cargo new demo && cd demo
添加依赖:
[dependencies]
boluo = "0.6"
tokio = { version = "1", features = ["full"] }
用以下内容覆盖src/main.rs
:
use boluo::response::IntoResponse;
use boluo::route::Router;
use boluo::server::Server;
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
let app = Router::new().mount(hello);
Server::new(listener).run(app).await.unwrap();
}
#[boluo::route("/", method = "GET")]
async fn hello() -> impl IntoResponse {
"Hello, World!"
}
运行项目:
cargo run
访问服务:
curl http://127.0.0.1:3000/
更多示例
在这里可以找到更多的示例代码。在示例目录中,你可以通过以下命令运行示例:
cargo run --bin hello
支持的最低Rust版本(MSRV)
支持的最低Rust版本为1.85.0
。
Demo
use std::convert::Infallible;
use std::sync::Arc;
use std::sync::atomic::{AtomicU32, Ordering};
use std::time::Duration;
use boluo::BoxError;
use boluo::data::Extension;
use boluo::extract::Path;
use boluo::handler::handler_fn;
use boluo::http::StatusCode;
use boluo::middleware::simple_middleware_fn;
use boluo::request::Request;
use boluo::response::{IntoResponse, Response};
use boluo::route::{RouteError, RouteErrorKind, Router, get};
use boluo::server::Server;
use boluo::service::{Service, ServiceExt};
use boluo::ws::WebSocketUpgrade;
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
// 构建路由
let app = Router::new()
.route("/", get(handler_fn(async || "Welcome to Boluo")))
.mount(ws)
.mount(calc)
.mount(count)
.or_else(handler_app_error)
.or_else(handler_any_error)
.with(simple_middleware_fn(log))
.with(Extension(Arc::new(AtomicU32::new(0))));
// 关机信号
let shutdown = async {
let _ = tokio::signal::ctrl_c().await;
Some(Duration::from_secs(30)) // 收到关机信号,30秒后强制关闭服务器
};
Server::new(listener)
.run_with_graceful_shutdown(app, shutdown)
.await
.unwrap();
}
// -----------------------------------------------------------------------------
// 处理程序
// -----------------------------------------------------------------------------
/// WebSocket,将接收到的消息原样返回
#[boluo::route("/ws", method = "GET")]
async fn ws(upgrade: WebSocketUpgrade) -> impl IntoResponse {
upgrade
.on_upgrade_error(|err| println!("websocket upgrade error: {err}"))
.on_upgrade(async |mut socket| {
while let Some(Ok(message)) = socket.recv().await {
let _ = socket.send(message).await;
}
})
}
/// 四则运算
#[boluo::route("/calc/{op}/{a}/{b}", method = "GET")]
async fn calc(Path((op, a, b)): Path<(String, f64, f64)>) -> Result<impl IntoResponse, AppError> {
match op.as_str() {
"add" => Ok(format!("{} + {} = {}", a, b, a + b)),
"sub" => Ok(format!("{} - {} = {}", a, b, a - b)),
"mul" => Ok(format!("{} * {} = {}", a, b, a * b)),
"div" => Ok(format!("{} / {} = {}", a, b, a / b)),
_ => Err(AppError::CalcOperationNotSupported),
}
}
/// 计数
#[boluo::route("/count", method = "GET")]
async fn count(Extension(cnt): Extension<Arc<AtomicU32>>) -> impl IntoResponse {
format!("{}", cnt.fetch_add(1, Ordering::Relaxed))
}
// -----------------------------------------------------------------------------
// 中间件
// -----------------------------------------------------------------------------
/// 一个简单的日志中间件
async fn log<S>(req: Request, service: &S) -> Result<S::Response, S::Error>
where
S: Service<Request, Response = Response>,
S::Error: std::fmt::Display,
{
println!("<< {:?} {} {}", req.version(), req.method(), req.uri());
let result = service.call(req).await;
match &result {
Ok(response) => println!(":: {}", response.status()),
Err(error) => println!("!! {error}"),
}
result
}
// -----------------------------------------------------------------------------
// 错误处理
// -----------------------------------------------------------------------------
/// 自定义错误
#[derive(Debug, thiserror::Error)]
enum AppError {
#[error("Operation not supported")]
CalcOperationNotSupported,
}
/// 处理任何错误
async fn handler_any_error(error: BoxError) -> Result<Response, Infallible> {
if let Some(e) = error.downcast_ref::<RouteError>() {
let status_code = match e.kind() {
RouteErrorKind::NotFound => StatusCode::NOT_FOUND,
RouteErrorKind::MethodNotAllowed => StatusCode::METHOD_NOT_ALLOWED,
};
return status_code.into_response();
}
StatusCode::INTERNAL_SERVER_ERROR.into_response()
}
/// 针对 AppError 类型的错误处理
async fn handler_app_error(error: BoxError) -> Result<Response, BoxError> {
let Some(e) = error.downcast_ref::<AppError>() else {
return Err(error);
};
match e {
AppError::CalcOperationNotSupported => {
(StatusCode::BAD_REQUEST, format!("{e}")).into_response()
}
}
}
1
共 3 条评论, 1 页
评论区
写评论不错不错,总代码行数也就几万行,适合学习阅读
设计思路类似axum,有中文注释,大拇指!
是的,就是菠萝
--
👇
Mike Tang: 菠萝?
菠萝?