我帮你把 ruwebframe 框架封装一下,核心思路是把 Salvo 的底层细节(路由构建、OpenAPI 文档、服务启动)全部隐藏到框架内部,对外暴露简洁的 API。这样业务代码只需要关注 Handler 和中间件,不用重复写样板代码。
封装后的框架结构
ruwebframe/
├── src/
│ ├── lib.rs # 框架入口,导出公共 API
│ ├── server.rs # 服务器启动封装
│ ├── router.rs # 路由构建 + OpenAPI 文档生成
│ ├── middleware.rs # 中间件辅助工具
│ └── prelude.rs # 预导入模块
1. lib.rs — 框架入口
// ruwebframe/src/lib.rs
pub mod server;
pub mod router;
pub mod middleware;
pub mod prelude;
pub use salvo;
pub use salvo::prelude::*;
pub use salvo::oapi::{self, OpenApi, Info, Server, SwaggerUi, ReDoc};
pub use ruconf::find_bean_ru_config;
pub use rupage::pagemodel::ru_result::RuResult;
2. prelude.rs — 预导入(用户只需 use ruwebframe::prelude::*)
// ruwebframe/src/prelude.rs
pub use crate::server::RuServer;
pub use crate::router::RuRouterBuilder;
pub use crate::middleware::{auth_middleware, AuthUser};
pub use salvo::prelude::*;
pub use salvo::oapi::{endpoint, ToSchema};
pub use crate::RuResult;
3. middleware.rs — 中间件封装
// ruwebframe/src/middleware.rs
use salvo::prelude::*;
use serde::{Serialize, Deserialize};
/// 鉴权后注入的用户信息
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AuthUser {
pub id: u64,
pub username: String,
pub role: String,
}
/// 鉴权中间件:检查 Authorization header
pub async fn auth_middleware(
req: &mut Request,
depot: &mut Depot,
res: &mut Response,
ctrl: &mut FlowCtrl,
) {
let token = req
.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
// 模拟 token 解析(实际项目中替换为 JWT 验证)
if token == "Bearer valid-token" {
let user = AuthUser {
id: 1001,
username: "rust-dev".into(),
role: "admin".into(),
};
depot.insert("current_user", user);
ctrl.call_next(req, depot, res).await;
} else {
res.status_code(StatusCode::UNAUTHORIZED);
res.render(Text::Plain("Token 无效"));
ctrl.skip_rest();
}
}
4. router.rs — 路由构建器(核心封装)
// ruwebframe/src/router.rs
use salvo::prelude::*;
use salvo::oapi::{OpenApi, Info, Server, SwaggerUi, ReDoc};
/// 路由构建器:封装路由注册 + OpenAPI 文档生成
pub struct RuRouterBuilder {
router: Router,
title: String,
version: String,
server_url: String,
}
impl RuRouterBuilder {
pub fn new(title: &str, version: &str) -> Self {
Self {
router: Router::new(),
title: title.to_string(),
version: version.to_string(),
server_url: "/api".to_string(),
}
}
/// 设置服务器基础路径
pub fn server_url(mut self, url: &str) -> Self {
self.server_url = url.to_string();
self
}
/// 添加路由
pub fn push(mut self, router: Router) -> Self {
self.router = self.router.push(router);
self
}
/// 构建最终路由(自动生成 OpenAPI 文档 + Swagger UI + ReDoc)
pub fn build(self) -> Router {
let doc = OpenApi::new(Info::new(&self.title, &self.version))
.merge_router(&self.router)
.add_server(Server::new(&self.server_url));
self.router
.unshift(doc.into_router("/api-doc/openapi.json"))
.unshift(
SwaggerUi::new("/api-doc/openapi.json")
.title(&format!("{} – SwaggerUI", self.title))
.into_router("/swagger-ui"),
)
.unshift(
ReDoc::new("/api-doc/openapi.json")
.title(&format!("{} – ReDoc", self.title))
.into_router("/redoc"),
)
}
}
5. server.rs — 服务器启动封装
// ruwebframe/src/server.rs
use salvo::prelude::*;
use crate::ruconf::find_bean_ru_config;
/// 服务器启动器:封装配置读取 + 服务启动
pub struct RuServer;
impl RuServer {
/// 从配置文件读取端口并启动服务
pub async fn serve(router: Router) {
let conf = find_bean_ru_config().expect("Failed to load config");
let port = conf.read_web_server().port;
let addr = format!("0.0.0.0:{}", port);
println!("🚀 服务已启动: http://{}", addr);
println!("📖 Swagger UI: http://{}/swagger-ui", addr);
println!("📖 ReDoc: http://{}/redoc", addr);
salvo::Server::new(TcpListener::new(&addr).bind().await)
.serve(router)
.await
.expect("Server failed to start");
}
/// 指定地址启动服务
pub async fn serve_at(router: Router, addr: &str) {
println!("🚀 服务已启动: http://{}", addr);
salvo::Server::new(TcpListener::new(addr).bind().await)
.serve(router)
.await
.expect("Server failed to start");
}
}
6. 用户业务代码(封装后的简洁写法)
use ruwebframe::prelude::*;
/// 公开接口:无需鉴权
#[endpoint(tags("公开接口"), summary = "Hello World")]
async fn hello() -> &'static str {
"Hello, Salvo!"
}
/// 受保护接口:需要鉴权
#[endpoint(
tags("受保护接口"),
summary = "获取受保护资源",
security(
("BearerAuth" = [])
)
)]
async fn rets() -> Json<RuResult<String>> {
Json(RuResult::<String>::default())
}
#[tokio::main]
async fn main() {
// 构建路由:链式调用,简洁清晰
let router = RuRouterBuilder::new("用户管理API", "1.0.0")
.server_url("/api")
// 公开路由
.push(Router::with_path("pub").get(hello))
// 受保护路由:挂载鉴权中间件
.push(
Router::with_path("rets")
.hoop(auth_middleware) // 中间件作用域
.get(rets),
)
.build();
// 启动服务:一行搞定
RuServer::serve(router).await;
}
封装前后对比
| 路由构建 | 手动 Router::new().push() + 手动 unshift 文档路由 | RuRouterBuilder::new().push().build() 链式调用 |
| OpenAPI 文档 | 手动写 OpenApi::new(Info::new(…)).merge_router().add_server() | 内部自动完成,用户无感知 |
| 服务启动 | 手动读配置 + TcpListener::new().bind().await + Server::new().serve() | RuServer::serve(router) 一行 |
| 中间件 | 手写 auth_middleware 函数 | 框架提供 auth_middleware,用户直接 .hoop(auth_middleware) |
| 代码行数 | ~60 行样板代码 | ~15 行业务代码 |
后续扩展方向
需要我继续补充"统一响应封装"或"错误处理"模块吗?
