AFaster:一个开箱即用的 Rust 高性能后端框架模板
基于 afast 构建,内置 25+ 业务模块,从微信支付到链路追踪,一行配置即可启用。
前言
在日常后端开发中,我们反复做的事情往往不是写核心业务逻辑,而是搭架子——接数据库、配 JWT、对接微信支付、接入 OSS 存储、写日志、搞限流……这些"胶水代码"每个项目都要来一遍,耗时且容易出错。
AFaster 就是为了解决这个问题而生的:它是一个基于 Rust 的后端框架模板,把常见的业务模块全部预置好,通过 Cargo feature 按需启用,通过 config.toml 统一配置,让你在 main() 里写几行代码就能跑起一个功能完备的后端服务。
use afaster::AFaster;
#[tokio::main]
async fn main() {
AFaster::new("config.toml".to_string()).await
.unwrap()
.run()
.await;
}
是的,就这么点代码。下面聊聊它背后的设计和能力。
设计理念
1. Feature-driven,按需组合
AFaster 的所有功能模块都通过 Cargo feature 控制。不需要微信支付?不启用 wx-pay-* 就行,编译产物不会包含任何相关代码。需要 Redis + PostgreSQL + 邮件发送?组合启用即可:
[dependencies]
afaster = { version = "0.0.3", features = ["redis", "db-postgres", "email"] }
这种设计带来了两个直接好处:
- 编译产物精简——未启用的模块不会被编译进来
- 依赖冲突可控——每个 feature 只拉取自己需要的依赖
2. 统一配置,链式构建
所有模块的配置统一放在 config.toml 中,结构清晰:
[backend]
host = "0.0.0.0"
port = 5000
[redis]
host = "127.0.0.1"
port = 6379
[wx_pay]
mch_id = "your_mch_id"
app_id = "your_app_id"
# ...
而在代码侧,AFaster 提供链式 Builder API 来注册回调、配置行为:
AFaster::new("config.toml".to_string()).await
.unwrap()
.with_github_oauth2(|g| g.with_github_callback(|(state, result, oauth_state)| {
async move {
// 处理 GitHub OAuth2 登录回调
Ok(result)
}
}))
.with_wx_virtual_pay(|w| w
.with_goods_deliver_callback(|(state, notify)| {
async move {
// 处理虚拟支付道具发货
Ok(afaster::wx_virtual_pay::WxPayNotifyResult::success())
}
})
)
.run()
.await;
3. 静态文件服务内置
前后端分离项目中,SPA 部署是刚需。AFaster 内置了静态文件服务模块,支持两种模式:
运行时目录模式——从磁盘读取:
AFaster::new("config.toml".to_string()).await
.unwrap()
.with_serve(Serve::from_dir("./dist").with_spa(true))
.run()
.await;
编译期嵌入模式——整个目录打包进二进制,部署时只需一个文件:
AFaster::new("config.toml".to_string()).await
.unwrap()
.with_serve(Serve::from_embedded(include_dir!("$CARGO_MANIFEST_DIR/dist")).with_spa(true))
.run()
.await;
SPA 模式下,未匹配的路径会自动回退到 index.html,前端路由不再 404。
功能模块全景
AFaster 目前内置 25+ 个功能模块,覆盖了后端开发的方方面面。按类别梳理如下:
认证与安全
JWT 令牌认证
# config.toml
[token]
expire = 3600
secret = "your-secret-key"
#[handler(desc("接口名称"), rate_limit("接口限流策略名称"))]
async fn api(state: State<AppState>, req: Data<Req>) -> Result<Resp> {
// 所有模块都可以通过 state 调用
// 生成 Token
let token = state.token.create_token(user_id, &user_data)?;
// 验证并提取数据
let claims = state.token.verify_token::<UserData>(&token)?;
}
Argon2 密码哈希
use afaster::argon2;
let hash = argon2::hash("my_password")?;
let ok = argon2::verify("my_password", &hash)?;
RBAC 权限管理
// 实现 RbacStore trait
impl RbacStore for MyStore {
fn check_permission(&self, user_id: i64, code: &str) -> Pin<Box<dyn Future<Output = Result<bool>> + Send + '_>> {
// 查询权限逻辑
}
}
// 在 handler 中使用
if !rbac.check_permission(user_id, "post:create").await? {
return Err(afaster::Error::custom(42803, "权限不足"));
}
限流
# config.toml
[[rate_limit.policies]]
id = "login"
max_requests = 5
window_seconds = 60
algorithm = "sliding_window"
key = "ip"
#[handler(desc("登录接口"), rate_limit("login"))]
async fn login(state: State<AppState>, req: Data<LoginReq>) -> Result<LoginResp> {
// 框架自动按 "login" 策略限流
}
HTTPS / WSS
# config.toml
[backend.tls]
port = 443
cert_path = "/etc/ssl/cert.pem"
key_path = "/etc/ssl/key.pem"
// 无需修改代码,框架自动启用 HTTPS
AFaster::new("config.toml".to_string()).await.unwrap().run().await;
微信生态
这是 AFaster 最用心的部分之一,覆盖了微信体系下的主流场景:
微信登录(小程序/APP/网页)
# config.toml
[wxlogin]
mini_id = "wx1234567890"
mini_secret = ""
app_id = "wx1234567890"
app_secret = ""
// 小程序登录
let result = state.wxlogin.mini_login(&code).await?;
let openid = result.openid;
// APP 登录
let result = state.wxlogin.app_login(&code).await?;
let access_token = result.access_token;
微信支付(H5/Native/APP/小程序/JSAPI)
# config.toml
[wx_pay]
mch_id = "1600000000"
app_id = "wx_your_app_id"
api_v3_key = "your_api_v3_key"
private_key = """-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
"""
serial_no = "your_cert_serial_no"
notify_url = "https://your-domain.com/api/wx-pay/notify"
// H5 支付
let result = state.wx_pay.prepay_h5(
"ORDER_20260603_001", "测试商品", 100,
"123.123.123.123", "Wap", "https://example.com",
).await?;
println!("h5_url: {}", result.h5_url);
// Native 支付
let result = state.wx_pay.prepay_native(
"ORDER_20260603_001", "测试商品", 100,
).await?;
println!("code_url: {}", result.code_url);
微信公众号
use afaster::wx_official::TemplateDataBuilder;
// 模板消息
let data = TemplateDataBuilder::new()
.value("name01", "张三")
.value("amount01", "¥100")
.build();
let msgid = state.wx_official.send_template_message(
"openid", // 接收者
"template_id", // 模板 ID
data, // 模板数据
Some("https://example.com"), // 跳转链接 (可选)
None, // 跳转小程序 (可选)
).await?;
// 网页授权
let result = state.wx_official.oauth2_access_token(&code).await?;
每种支付方式都有独立的 feature 和回调机制,互不干扰。
云服务集成
阿里云 OSS
# config.toml
[oss]
access_key_id = ""
access_key_secret = ""
region = "cn-hangzhou"
bucket = ""
let url = state.oss.get_signed_download_url("abc123.png").await?;
let url = state.oss.get_signed_upload_url("abc123.png", "image/png").await?;
支付宝电脑网站支付
# config.toml
[ali_pay]
app_id = "2014072300007148"
private_key = """-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
"""
alipay_public_key = """-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
"""
notify_url = "https://example.com/ali/pay/notify"
use afaster::ali_pay::PagePayRequest;
let html = state.ali_pay.page_pay(&PagePayRequest {
out_trade_no: "ORDER_001".into(),
total_amount: "100.00".into(),
subject: "测试商品".into(),
..Default::default()
}).await?;
阿里云短信
# config.toml
[sms_ali]
access_key_id = ""
access_key_secret = ""
sign_name = "你的签名"
template_code = "SMS_123456"
let resp = state.sms_ali.send_code("13800138000", "1234").await?;
腾讯云 COS
# config.toml
[cos]
secret_id = ""
secret_key = ""
bucket = "mybucket-1250000000"
region = "ap-guangzhou"
let url = state.cos.get_signed_download_url("abc123.png").await?;
let url = state.cos.get_signed_upload_url("abc123.png", "image/png").await?;
腾讯云短信
# config.toml
[sms_tencent]
secret_id = ""
secret_key = ""
sdk_app_id = "1400000000"
sign_name = "你的签名"
template_id = "123456"
let resp = state.sms_tencent.send_code("13800138000", "1234", Some("5")).await?;
腾讯地图
# config.toml
[tmap]
key = "your_tencent_map_key"
let res = tmap.geocoder("北京市海淀区", Some("北京")).await?;
let res = tmap.direction_driving("39.984154,116.307490", "39.904989,116.405285", None, None).await?;
数据层
多数据库支持(PostgreSQL / SQLite / MySQL)
# config.toml
[postgres]
host = "127.0.0.1"
port = 5432
user = "postgres"
pass = ""
name = "afaster"
// 单数据库时
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_one(state.db.pool())
.await?;
// 多数据库时
let pg_pool = state.db.pg();
let sqlite_pool = state.db.sqlite();
Redis / Valkey
# config.toml
[redis]
host = "127.0.0.1"
port = 6379
state.redis.set("key", "value", Some(3600)).await?;
let val: Option<String> = state.redis.get("key").await?;
state.redis.hset("user:1", "name", "Alice").await?;
let name: Option<String> = state.redis.hget("user:1", "name").await?;
内存 KV
use afaster::memkv::MemKV;
let kv = MemKV::new();
kv.set("name", b"hello".to_vec(), Some(Duration::from_secs(60))).await?;
let v: Option<Vec<u8>> = kv.get("name").await?;
布隆过滤器
# config.toml
[bloom]
expected_items = 100000
false_positive_rate = 0.01
auto_check_key = "ip"
use afaster::bloom::BloomFilter;
// 手动模式
if bloom.check("some_key") {
return Err(afaster::Error::custom(41001, "Duplicate request"));
}
bloom.add("some_key");
文件处理
本地文件服务
# config.toml
[file]
root = "./uploads"
max_size = 10485760
allowed = ["jpg", "png", "gif", "pdf"]
use afaster::FileService;
let fs = FileService::new(&config.file)?;
let info = fs.upload("docs/readme.txt", data)?;
let data = fs.download("docs/readme.txt")?;
let url = fs.url("docs/readme.txt")?;
Excel / CSV
use afaster::Excel;
// 读取 Excel/CSV(自动识别格式)
let data = Excel::read_path("data.xlsx", None)?;
let data = Excel::read_path("data.csv", None)?;
// 读取为 JSON
let json = Excel::read_to_json("users.xlsx", None)?;
// 写入 Excel
let bytes = Excel::write_xlsx(&headers, &rows)?;
PDF 生成
use afaster::{Pdf, TextStyle};
let font = std::fs::read("fonts/SourceHanSansSC-Regular.otf")?;
let bytes = Pdf::new("发票")
.font_regular(&font)
.text("增值税普通发票", TextStyle::title(), 60.0, 270.0)
.text("金额: ¥12,493.00", TextStyle::body().bold(), 15.0, 230.0)
.finish()?;
std::fs::write("invoice.pdf", bytes)?;
图片生成
use afaster::{Image, ImageTextStyle, ImageColor};
let font = std::fs::read("fonts/SourceHanSansSC-Regular.otf")?;
let bytes = Image::new(800, 600)
.font_regular(&font)
.background(ImageColor::white())
.draw_rect(0, 0, 800, 80, ImageColor::blue())
.draw_text("用户报告", 20, 20, ImageTextStyle::title().color(ImageColor::white()))
.finish()?;
std::fs::write("poster.png", bytes)?;
消息通知
邮件发送
# config.toml
[email]
default = "main"
[[email.accounts]]
id = "main"
host = "smtp.qq.com"
port = 465
user = ""
pass = ""
from = ""
name = ""
state.email.send("to@example.com", "标题", "<h1>正文</h1>", &[]).await?;
state.email.send_text("to@example.com", "标题", "纯文本正文", &[]).await?;
推送服务(个推/极光/小米)
# config.toml
[push.getui]
app_id = ""
app_key = ""
master_secret = ""
use afaster::push::{PushTarget, Notification};
let target = PushTarget::Cid("regid".to_string());
let notification = Notification::new("标题", "内容").url("https://example.com");
state.push.getui.send(target, notification, None).await?;
SSE 实时推送
use afaster::Socket;
// 注册 SSE 连接
let conn_id = socket.register_sse(Some("user_1001"), sse_sender, None).await;
// 按用户 ID 推送
socket.send_to_id("user_1001", "新消息通知").await;
// 按分组推送
socket.send_to_group("vip_users", "专属优惠").await;
// 广播
socket.broadcast("系统公告").await;
工具集
Snowflake ID 生成器
# config.toml
[snow]
worker_id = 1
datacenter_id = 1
let id = state.snow.next_id().await;
let id_str = state.snow.next_id_str().await;
let order_no = state.snow.next_id_prefix("ORD").await;
定时任务
AFaster::new("config.toml".into()).await?
.with_scheduler(|s| {
s.add_task("cleanup", "*/5 * * * *", |state, name, times| async move {
println!("[{}] 第 {} 次执行", name, times);
}).unwrap();
s
})
.run()
.await;
链路追踪
# config.toml
[tracing]
service_name = "afaster"
db_path = "tracing.db"
// 无需修改代码,框架自动为每个请求创建 Span
// 启动后访问 /tracing 查看 Web UI
AFaster::new("config.toml".to_string()).await.unwrap().run().await;
高德地图
# config.toml
[amap]
key = "your_amap_key"
let res = amap.geocode("北京市朝阳区阜通东大街6号", Some("北京")).await?;
let res = amap.direction_walking("116.397428,39.90923", "116.407428,39.91923").await?;
实战:用 AFaster 搭一个博客系统
AFaster 的 example/ 目录下就有一个完整的多人博客系统示例,展示了如何用框架快速搭建业务。它包含三个 Service:
user:公开接口(注册、登录、浏览文章)user_backend:用户后台(管理自己的文章、评论)admin:管理员接口(审核、用户管理、分类标签管理)
通过 RBAC 实现权限控制,每个接口都有明确的权限码,框架自动完成鉴权:
// RBAC 存储实现(SQLite)
impl RbacStore for SqliteRbacStore {
fn check_permission(&self, user_id: i64, permission_code: &str)
-> Pin<Box<dyn Future<Output = Result<bool>> + Send + '_>>
{
// 查询用户角色关联的权限表
}
}
这个示例涵盖了数据库操作、JWT 认证、RBAC 权限、分页查询等典型后端场景,是很好的学习参考。
可交互的 API 调试文档
错误码设计
AFaster 采用 5 位数字错误码,设计巧妙:
- 第 1 位:
4= 用户错误,5= 内部错误 - 第 2-3 位:模块编号(如
28= 某个具体模块) - 第 4-5 位:模块内序号
例如 42801 表示"模块 28 的第 1 个用户错误",52803 表示"模块 28 的第 3 个内部错误"。这种设计让错误码既有序又可读,排查问题时一眼就能定位到模块。
链路追踪
AFaster 的链路追踪模块(trace)值得一提。它支持三种后端:
trace-sqlite:存储到 SQLite,适合单机部署trace-http:通过 HTTP 上报,适合集中式收集trace-tcp:通过 TCP 上报,性能更好
更贴心的是,它还内置了一个 Web UI,可以直接在浏览器中查看链路瀑布图、Span 树、耗时分布等。对于排查性能问题非常有帮助。
代码生成
启用 afast-ts / afast-js / afast-kt / afast-rs 等 feature 后,AFaster 可以自动生成客户端代码(TypeScript / JavaScript / Kotlin / Rust),让前后端对接更加顺畅。这意味着你定义好 Service 接口后,前端可以直接拿到类型安全的 SDK。
总结
AFaster 不是要替代 Actix-web 或 Axum 这样的通用框架,而是在它们之上提供一层业务模板。它的定位很明确:
- ✅ 快速搭建功能完备的后端服务
- ✅ 内置微信生态、云服务、支付等中国特色业务模块
- ✅ Feature-driven 按需组合,不引入多余依赖
- ✅ 统一配置 + 链式 API,降低心智负担
- ✅ 内置链路追踪、限流、RBAC 等生产级能力
如果你正在用 Rust 做后端开发,且项目涉及微信生态或常见的业务模块集成,AFaster 值得一试。它能帮你跳过大量的"轮子搭建"阶段,直接进入核心业务开发。
AFaster 基于 MIT 协议开源,欢迎 Star 和 PR。