是的,我要向大家推荐一个项目,一个 rust 验证器项目,在开始之前,请允许我先讲下 axum 项目,是这个项目给了我启发,使我恍然大悟,原来 rust 还可以这么写,原来静态类型的语言也可以这么灵活。写过 rust lib 库的同学可能有体会,就是自己写的函数,参数要么是标准库里的基础类型,要么是自己的类型,如果别人使用的话,没法使用他们自己构造的类型,有些时候感觉很受限制。但是了解了 axum 项目后,才发现是我的思路没有打开。
先给大家看一个例子:
let app = Router::new()
// `POST /` goes to `root`
.route("/", post(root))
// `POST /users` goes to `create_user`
.route("/users", post(create_user));
async fn root() -> &'static str {
"Hello, World!"
}
async fn create_user(
Json(payload): Json<CreateUser>,
) -> (StatusCode, Json<User>) {
todo!()
}
我们可以在代码中发现,root 和 create_user 是签名完全不同的两个函数,但是他们却可以同时作用于 route 方法的第二个参数,刚看到这个代码的时候,真的让人眼前一亮,仿佛发现了新大陆一样。
也许表单验证也应该像这个一样,每个字段有自己的验证要求,而且它们能够有机的组合在一起,一起验证,一起处理返回的消息
开源是个好东西,可以学习别人的项目是如何实现的,根据他们的经验,归纳总结,吸收思路注入到自己的项目中,然后回馈社区。
经过了一两个月的打磨,一款新的表单验证器的雏形已经出来了:
let validator = Validator::new()
.rule("name", Required.and(StartWith("hello")))
.rule("age", custom(age_limit))
.message([
("name.required", "name is required"),
("name.start_with", "name should be starts with `hello`"),
]);
let person = Person {
name: "li",
age: 18,
};
fn age_limit(n: &mut u8) -> Result<(), Message> {
if *n >= 25 && *n <= 45 {
return Ok(());
}
Err("age should be between 25 and 45".into())
}
这里要感谢一下 asuper0 (github.com) 同学的热情参与。
验证器跟上面的 web 框架还是有些不同的,验证器其实是由很多不同的规则组合在一起,来处理一个结构体中各个字段是否合规范。关于这些规则,其实有一些是很常用的,比如 Required
(必填),Trim
, Range
(范围),Email
等等,这些可以作为实现了某一个 trait 的不同 struct,当然也必须要有自定义的方式,那就是封装成一个函数。
于是,就有了如下这些组合方式
用法 | 说明 |
---|---|
Required |
one rule |
Required.and(StartsWith("foo")) |
multi rules |
Required.and(StartsWith('a')).bail() |
multi rules and bail |
custom(my_handler) |
custom handler rule |
Required.custom(my_handler) |
rule and handler rule |
Not(StartsWith("foo")) |
negative rule |
Required.and(Not(StartsWith("foo"))) |
negative rule |
所有的这些组合,都实现了 IntoRuleList<M>
这个 trait,所以,它们可以作为同一个函数的同一个参数
完整项目地址: https://github.com/tu6ge/valitron
Ext Link: https://github.com/tu6ge/valitron
评论区
写评论还没有评论