前言#
昨天我们建完了表,然后接入到了项目中,今天我们来做一些全局配置
项目地址:
https://github.com/1714080902120/rust_rocket_crud_demo由于我是先实现完再写的这篇文章,如果有些地方无法运行,可以看下我项目里的代码。
目前我还在往全栈的方向学习,所以如果看的不顺眼,请多多包涵。如果觉得那里可以改进,麻烦评论区说下,谢谢~
全局配置#
我们直接在src文件夹下创建一个config的文件夹,然后在里面老规矩创建一个mod.rs文件。
use rocket::{
fairing::AdHoc,
figment::{
providers::{Format, Serialized, Toml},
Figment,
},
Config,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
pub struct MyConfig {
pub token_field: String,
pub token_key: String,
pub exp: u64,
}
impl Default for MyConfig {
fn default() -> Self {
Self {
token_field: String::from("_token"),
token_key: String::from("dan"),
exp: 24 * 60 * 60, // one day
}
}
}
pub fn get_custom_figment() -> Figment {
Figment::from(Config::default())
.merge(Toml::file("Rocket.toml").nested())
.merge(Serialized::defaults(MyConfig::default()))
}
pub fn init_my_config() -> AdHoc {
AdHoc::config::<MyConfig>()
}
Figment[1] :我们之前学rocket的时候有说到过,这个trait是用来创建config用的,我们自定义config的时候就会用到。
这里的MyConfig就是我们的自定义配置,我在这里配置了三个项:
token_field:response插入到header中的字段名,由于很多地方都有用到这个名字,所以我把这个抽出来了。token_key:这个自然就是我们的token的加密的key。exp:token过期的时间间隔,我这里初始设置为一天。
我给MyConfig实现了Default,这样我在使用到这个MyConfig赋值的地方就不再需要手动去赋值,比如get_custom_figment方法中。
get_custom_figment方法中我和并了默认的配置 + Rocket.toml里的配置 + MyConfig里的配置。
注意这里MyConfig需要实现Serialize,因为合并的过程需要序列化。
init_my_config:这个方法将我们的MyConfig的配置暴露到全局,这样我们就可以在任意生周期使用到它了,可以从它里面拿我们想要的字段。
实际上这种方案不是很好,这种按理说应该是常量,常量放在一个公共文件中即可。这里主要是为了学习如何自定义配置内容。
然后回到main函数中,我们导入刚才的config这个mod,然后引入上面这两个方法
// ...
mod config;
use config::{get_custom_figment, init_my_config};
// ...
#[launch]
fn rocket() -> _ {
rocket::custom(get_custom_figment())
.attach(init_my_config())
// ...
}
这里我们将rocket::build切换成了rocket::custom[2],这样做是为了插入我们自定义的配置
通用类型#
我们response给前端的类型需要是一个固定的类型,这样写起来会方便一些。
use serde::{Deserialize, Serialize};
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct RtData<T> {
pub success: bool,
pub rt: Rt,
pub data: T,
pub msg: String,
}
// 自定义错误类型状态
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
pub enum Rt {
AuthFail,
AuthSuccess,
Success,
Fail,
Error,
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DataType {
DefaultSuccessData(()),
Fail
}
impl<T: Serialize> RtData<T> {
pub fn to_string(mut self) -> String {
serde_json::to_string(&mut self).unwrap()
}
} 其中data是一个泛型字段,这样我们就可以动态组装数据发送给前端。
而rt则代表着当前传输的状态:
AuthFail:表示token验证失败AuthSuccess:表示验证成功,用于登录成功时Success:通用变体,表示响应成功Fail:通用变体,表示响应失败Error:专门用于500,即内部出错的场景
由于data字段是需要灵活的,所以并不能写死它的类型,不过我们可以使用枚举类型来创建一个大概的类型,这样我们后面有些地方就不用去重复的实现Responder[3]这个trailt了。
这里还必须给RtData实现一个Serialize这个trait,这么做是为了让这个RtData支持序列化为后面response提供类型支持。这里用到了前面安装的serde_json这个trait。
我们后面还需要给这个RtData实现一个Responder,这样才能真正的支持response 。
捕获器#
我们需要对一些错误场景做统一处理,比如404、400以及500这三种
我们在src文件夹下直接创建一个catcher.rs文件
use std::path::Path;
use crate::types::{ FailureData, RtData, rt_type::Rt };
use rocket::{
serde::json::{serde_json::json, Value}, fs::NamedFile,
catch,
};
#[catch(400)]
pub fn bad_request_catcher() -> Option<RtData<FailureData>> {
Some(RtData {
success: false,
rt: Rt::Fail,
msg: String::from("get wrong params"),
data: FailureData(())
})
}
#[catch(404)]
pub async fn not_found_catcher() -> Option<NamedFile> {
NamedFile::open(Path::new("static/404.html")).await.ok()
}
#[catch(500)]
pub fn error_catcher() -> Option<Value> {
Some(json!({
"success": false,
"rt": -2,
"msg": String::from("server internal error"),
"data": FailureData(()),
}))
}
404的场景我们还需要提供对应的页面,对,就是页面,毕竟路径解析可能是错误的,这个时候前端处理不到,自然就需要在后端提供html。
我们在src”外“创建一个和src同级的文件夹static,在里面新建404.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>page 404</title>
</head>
<body>
页面找不到了~
</body>
</html> 这样就可以了
有一点需要注意,那就是识别的路径,从现在开始,我们不能再通过cargo run -p即workspace的方式运行我们的binary了,因为这会导致根路径识别失败。
最后别忘了在main.rs中注册
// ...
mod catcher;
// ...
use catcher::{bad_request_catcher, error_catcher, not_found_catcher};
#[launch]
fn rocket() -> _ {
rocket::custom(get_custom_figment())
// ...
.register(
"/",
catchers![error_catcher, not_found_catcher, bad_request_catcher],
)
// ...
}
这样我们在路由那边只需要return Err(Status::BadRequest)等它就会自动触发对应的捕获器了。
lib#
在src文件夹里我们还需要新建一个lib.rs,用于导入导出mod,为什么要这么做呢?因为我们需要在一个文件夹中使用到另一个文件夹的内容,那么就需要自己从lib中导出,这样就能在别的文件夹中use。
pub mod types;
pub mod catcher;
pub mod db;
pub mod state;
pub mod auth;
pub mod article;
pub mod config;
pub mod log; // 未实现上面这个是最终的,后面实现路由的过程中我就不放出来了。
基础的一些配置就写到这里,我们之后还会进行拓展,下一章我们来处理token相关的。
总结#
莫得
参考#
- ^figment https://docs.rs/figment/0.10.8/figment/struct.Figment.html
- ^rocket::custom https://api.rocket.rs/v0.5-rc/rocket/struct.Rocket.html#method.custom
- ^rocket::response::Responder https://api.rocket.rs/v0.5-rc/rocket/response/trait.Responder.html
发布于 2023-06-07 18:00・IP 属地广东
