Miko v0.7 发布:我写的一个 Rust Web 框架,虽然还是个玩具
断断续续写这个框架有一段时间了。起初只是因为在 Rust 里写 Web 服务时,总觉得要在"性能"和"开发体验"之间做妥协。Actix 和 Axum 性能很强,但每次写 Handler 都要手动注册路由,或者搞复杂的 State 传递,我就忍不住怀念 Spring Boot 或者 NestJS 那种"自动装配"的感觉。
于是就有了exum(虽然现在断更了,因为exum是依赖于axum的补丁类似的,写起来总有东西不好实现,所以想着自己搞一个玩玩得了,正好当时没什么事,还正好在学Rust)
于是就有了 Miko。
这只是我的个人作品,现阶段肯定不推荐大家用在严肃的生产环境里(除非你胆子很大并且愿意自己修 Bug)。但如果你想在 Rust 里体验一下"约定优于配置"和"宏魔法",或者想写个 Side Project 玩玩,Miko 可能会让你觉得挺有意思。
最近发了 v0.7 版本,正好跟大家聊聊都更新了啥,顺便介绍下怎么玩。
快速上手:真的很快
既然主打开发体验,那 Hello World 必须足够简单。你不需要手动创建 Router,也不用显式启动 Server,只需要定义 handler 和一个宏: (甚至返回类型都不用指定,发现你没指定会自己加个impl IntoResponse)
rust
use miko::*;
use miko::macros::*;
#[get("/")]
async fn hello() {
"Hello, Miko!"
}
#[miko]
async fn main() {
// 啥都不用写,框架自动扫描路由并启动
}
运行 cargo run,打开 localhost:8080,搞定。
实时通讯:从未如此简单
由于一些神奇的操作,貌似对实时通讯(SSE 和 WebSocket)做了大幅简化。
SSE (Server-Sent Events)
现在,你只需要返回一个闭包,框架就会自动把它放到后台任务里跑,甚至连 Response 都不用自己构造:
rust
#[get("/events")]
async fn events() -> impl IntoResponse {
// 直接返回一个闭包,接受 sender 参数
|sender: SseSender| async move {
for i in 0..5 {
// 发送数据,or_break() 会在客户端断开时自动停止任务(记得加上sse参数才能捕获这个断开,就#[miko(sse)])
sender.send(format!("Count: {}", i)).await.or_break();
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
}
}
WebSocket
WebSocket 也一样,不用自己处理 Upgrade Header,直接用 spawn_ws_event:
rust
#[get("/ws")]
async fn ws(mut req: Req) -> AppResult<Resp> {
// 自动处理握手,并在回调中处理连接
spawn_ws_event(|mut io| async move {
while let Some(Ok(msg)) = io.next().await {
if msg.is_text() {
io.send("Received!").await.unwrap();
}
}
}, &mut req, None).map_err(AppError::from)
}
终于不用手写 Tower Layer 了
在 Rust Web 开发里,中间件(Middleware)一直是个门槛。虽然 tower 的 Layer 抽象很美妙,但要自己实现 Service trait 真的很繁琐。
我之前也是深受其苦,所以这次加了个大杀器:#[middleware] 宏。
现在写个鉴权中间件或者日志中间件,跟写普通函数没啥区别:
rust
#[middleware]
async fn my_logger(#[config("app.name")] app_name: String) -> AppResult<Resp> {
println!("Request to {}", app_name);
// 继续执行下一个 handler
_next.run(_req).await
}
它会自动编译成一个标准的 tower::Layer。你能直接在参数里注入依赖、读取配置,甚至能直接处理错误。
测试,要快,还要简单
以前做集成测试,总得绑定个本地端口,经常遇到端口冲突,或者因为网络协议栈导致测试跑得慢。
所以我搞了个 TestClient。它能在同一个进程里模拟 HTTP 请求,直接把请求分发到路由处理逻辑,不走网络层。
配合 #[miko(build)] 宏,现在的测试代码可以写得很干净:
rust
#[tokio::test]
async fn test_api() {
// 直接复用 main.rs 里的构建逻辑
let app = app::create_app().await;
let client = app.test_client();
client.get("/api/users")
.send().await
.assert_status(200);
}
其他一些实用的改进
- Panic 也能兜底了:Handler 崩了会返回 500 错误,服务还能接着跑。
- 依赖注入变聪明了 :支持更复杂的依赖关系,还加了个
prewarm参数,可以让数据库连接池在启动时就预热好。 - 生态整合 :
utoipa(OpenAPI) 和garde(验证库) 直接 re-export,开箱即用。 - 优雅停机:也是经过好久之后终于端上来了,暂时是最大等待30s
试试看?
代码都在 GitHub 上:github.com/isyuah/miko
目前版本是 v0.7.0。如果你对它感兴趣,欢迎 cargo add miko --features full 玩一玩。
如果你在使用过程中遇到了 Panic,或者觉得哪里设计得很蠢,欢迎提 Issue 喷我(或者帮我修一下),毕竟这就是开源的乐趣嘛。
叠个甲(,我才刚学Rust没多久,轻喷~