Roket官网
官方是english文档,看的很累~
cargo new 项目名 --bin
Cargo.toml
[dependencies]
rocket = "0.5.0-rc.1"
Hello,world! src/main.rs
#[macro_use] extern crate rocket;
#[get("/")] // 路由属性
fn index() -> &'static str {//请求处理程序
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])//挂载
}
强烈建议使用
VSCode
+rust-analyzer
(插件) +Better TOML
(插件)
IDEA
的Rust
插件并不友好,瞎报错,你没错也报错,而且并不支持Rocket
框架的提示
响应类型必须实现 Responder(可派生)
#[get("/")]
fn index() -> T { /* ... */ }//T 需要实现Responder
&str
,String
,&[u8]
,Vec
,File
,()
,Option
,Result
都实现了 Responder
//例如
#[get("/")] //是不是和java的Spring框架的注解很像,其实它更加方便好用,让我们来慢慢了解它
需要实现 FromParam,&str,String,bool,i32,…基本类型都实现了它
程序1
use std::path::PathBuf;
use rocket::{request::FromRequest};
use rocket::http::{SameSite,CookieJar,Cookie};
use rocket::Request;
#[macro_use] extern crate rocket;
#[get("/b/" , rank = 3) ]
fn get_f1(a:&str) {
println!("str:{}",a);
}
#[get("/b/", rank = 0 )]
fn get_f2(a:i32) {
println!("i32:{}",a);
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/cs", routes![get_f1,get_f2])
}
rank
属性越小越优先匹配
如果匹配失败,则去匹配下一个,官方把这种行为称呼为转发
如果相同路径下,省略了rank
就会终止程序,相同的rank
也会终止程序
需要注意的一点:
&str
不会拒绝任何匹配,比如:i32
会拒绝非数字字符串匹配,由于&str
本身就是字符串,所以它不会拒绝任何匹配.那么&str
的优先级应该小一点,给其他函数一个机会
// 请求 => 输出
http://127.0.0.1:8000/cs/b/0 => i32:0
http://127.0.0.1:8000/cs/b/abc => str:abc
我理解的不是很明白,就不来误导大家了,请看官方文档默认rank
官方称呼为段守卫
段守卫
的类型必须实现 FromSegments
接着上面的程序1
,加入以下代码
#[get("/b/" , rank = 1)]
fn get_f3(a:PathBuf){
for b in a.iter() {
print!("{}/",b.to_str().unwrap());
}
}
//修改为:
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/cs", routes![get_f1,get_f2,get_f3])
}
// 请求 => 输出
http://127.0.0.1:8000/cs/b/0 => i32:0
http://127.0.0.1:8000/cs/b/0/0 => 0/0/
http://127.0.0.1:8000/cs/b/abc => abc/
http://127.0.0.1:8000/cs/b/abc/123 => abc/123/
很明显,它并没有匹配到get_f1
,因为get_f3
优先级更高,且匹配成功
就是字面意思,忽略一个或多个段
语法:
#[get("/a/<_>")] //忽略1个
#[get("/a/<_>/<_>")] //忽略2个
#[get("/a/<_>/c")] //忽略1个
#[get("/a/<_..>")] //忽略多个
请求守卫 顾名思义, 请求保护可防止处理程序根据传入请求中包含的信息被错误调用
请求守卫需要实现FromRequest
请求守卫总是按从左到右的声明顺序 进行验证,如果有一名守卫失败,则不会去尝试其他守卫
程序2
use std::path::PathBuf;
use rocket::http::{SameSite,CookieJar,Cookie};
use rocket::request::{self, Outcome, Request, FromRequest};
#[macro_use] extern crate rocket;
#[get("/<_..>")]
fn get_f1(ABC守卫:ABC) -> &'static str {
println!("get_f1:{}",ABC守卫.0);
"get_f1"
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![get_f1])
}
struct ABC(String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ABC {
type Error = ();
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let uri_string = req.uri().to_string();
println!("ABC守卫{}", uri_string);
if uri_string == "/a/b/c"{
Outcome::Success(ABC("abc".to_string()))//成功
} else {
Outcome::Forward(())//转发
//Outcome::Failure() // 失败
}
}
}
//请求 => [输出1,输出2,...] => 响应
http://127.0.0.1:8000/a/b/c => [ABC守卫/a/b/c,get_f1:abc] => get_f1
http://127.0.0.1:8000/a/b => [ABC守卫/a/b] => 404
看起来和java的Srping框架的注入意思差不多,只不过Rocket主要是起到守卫作用
CookieJar是一个重要的内置请求守卫,它允许您获取、设置和删除 cookie (确实很像注入,哈哈,注意它和注入的本质是完全不同的哦~)
可以通过format
属性来指明数据格式
#[post("/", format = "application/json")]
注意它只会匹配一样的格式,比如:
#[post("/" , format = "json")]
fn get_f1() -> &'static str {
"json"
}
#[post("/" , format = "text")]
fn get_f2() -> &'static str {
"text"
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![get_f1,get_f2])
}
//(请求格式)请求 => 响应
(text/plain)http://127.0.0.1:8000/ => text
(application/json)http://127.0.0.1:8000/ => json
(none)http://127.0.0.1:8000/ => 404
上面的json
和text
是简写,简写文档
想要获取Body数据,请使用 请使用 data
属性 ,T类型必须实现 FromData
任何实现的类型 FromData
也称为 数据守卫
#[post("/" , data = "")]
fn get_f1(a: T) -> &'static str { // T类型必须实现 FromData
"json"
}
#[post("/" , data = "")]
fn get_f1(a:ABC) -> String {
a.0
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![get_f1])
}
struct ABC(String);
#[rocket::async_trait]
impl<'r> FromData<'r> for ABC {
type Error=();
async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> data::Outcome<'r, Self> {
let stream = data.open(256.mebibytes());
let a = stream.into_string().await.unwrap().into_inner();
println!("{}",&a);
data::Outcome::Success(ABC(a))
}
}
// 请求 {(数据格式)Body} => 输出 =>响应
http://127.0.0.1:8000/ {(text/plain)abc} => abc => abc
http://127.0.0.1:8000/ {(任何格式)任何数据} => 任何数据 => 任何数据
注意!使用JSON需要在Cargo.toml
中开启
rocket = { version = "0.5.0-rc.1",features = ["json"] }
serde = "1.0.136" # 这玩意提供派生宏,方便的很
JSON数据守卫
,因为它也实现了FromData
T 类型 接受请求必须实现 Deserialize
T 类型 反馈响应必须实现 Serialize
它们俩都有派生宏
具体看下面的例子
#[post("/" , format = "json",data = "")] //format = "json" 建议加上去,毕竟你必须得获取json格式
fn get_f1(a : Json<ABC>) -> Json<ABC> {
println!("{:?}",a.into_inner());
Json(ABC {
a:123,
b:true,
c:String::from("鸡尼太美~O~baby")
})
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![get_f1])
}
#[derive(Deserialize, Serialize,Debug)]
struct ABC{
a:i32,
b:bool,
c:String
}
// 请求 {(数据格式)Body}
// => 输出
// => 响应
http://127.0.0.1:8000/ {(application/json){"a": 789,"b": true,"c": "abc"}}
=> ABC { a: 789, b: true, c: "abc" }
=> {"a":123,"b":true,"c":"鸡尼太美~O~baby"}
http://127.0.0.1:8000/ {(application/json){"a": "abc","b": true,"c": "abc"}}
=>
=> 404 //因为a的类型不匹配
你需要改一下配置文件Rocket.toml
[default]
temp_dir = "public" # 修改默认路径 到项目文件夹里 ,我linux的默认路径是 /tmp , std::env::temp_dir()可以查看默认路径
limits.file = "50MB" # 文件大小限制
其他的比较简单,也很方便,直接看文档吧
不懂TOML的话,可以看这篇文档
Form
T 类型需要实现 FromForm (可派生)
FromForm
默认情况下解析是 宽松 的 ,Form
即使它包含额外的、重复的或缺失的字段,它也会从传入的表单中成功解析。 多余的或重复的将被忽略——不会对字段进行验证或解析——缺失的字段会在可用时用默认值填充。 要更改此行为并使表单解析 严格 ,请使用 Form>数据类型,如果有任何额外或缺失的字段,则无论默认值如何,都会发出错误
Strict
也可用于使单个字段严格,同时保持整体结构和剩余字段宽松
#[derive(FromForm)]
struct ABC{
a : Strict<bool> ,
b : bool
}
LenientStrict
就是严格的了,可以再次通过Lenient
变回宽松
#[field(default = 表达式 , name = 字符串 )
默认值
如果表达式是None
将会取消默认值,如果不是则会表达式.into()
取消默认值也就意味着,如果没有值就会出错
重命名
#[field(name = uncased("aBc"))] //不区分大小写
注意使用重命名后,原来的名字会失效,如果需要,可以在加一个 过程宏
struct ABC{
#[field(name = "酷炫小A")]
#[field(name = "a")]
a:i32
}
#[field(validate = 表达式)]
表达式必须满足 返回 Result<(), Errors<'_>>
,很明显Ok(())
就是验证成功
点我~我这有一堆常用的函数
你还可以通过 self
来引用自身
#[derive(FromForm)]
struct ABC<'a> {
#[field(default = "abc" , name = "a")]
按钮:&'a str,
#[field(default = None , name = "b" , validate = eq(self.按钮))]
文本:&'a str,
}
不难,但是语法比较杂乱,想要了解完整版就看官方文档吧
#[derive(FromForm)]
struct ABC{
a:A,
b:B,
c:C
}
#[derive(FromForm)]
struct A {
a:String
}
#[derive(FromForm)]
struct B{
b:Vec<String>
}
#[derive(FromForm)]
struct C {
c:HashMap<String,String>
}
解剖可以通过.
或[]
比如 a.a=abc
或 a[a]=abc
对于集合来说, 可以理解成 .key
或 [key]
而非是解剖对象
比如 b.b.0=abc
或 b.b[0]=abc
,其实List集合,它并不关注key
List集合还可以写成 b.b.a=abc
或者 b.b=abc
,因为它不关注key,只关注顺序,如果key重复,那么忽略其他值,只取第一个值
比如
"b.b=a&b.b=b&b.b=c" == vec!["a","b","c"]
"b.b.a=a&b.b.a=qq&b.b.b=b&b.b.c=c" == vec!["a","b","c"]
Map才关注key
比如 c.c.key=abc
或 c.c[key]=abc
, 一样的key重复,忽略其他的,取第一个值
"c.c.a=abc&c.c[b]=abc" == {"a":"abc" , "b":"abc"}
它给的灵活度非常高,我建议,解剖用.
,List集合用.数字
,Map集合用[key]
Contextual
pub struct Contextual<'v, T> {
pub value: Option<T>,
pub context: Context<'v>,
}
它是这么说的,咱也不知道,咱也懒得看
有兴趣的话,可以自己看看
#[get("/?&")] //其他不匹配的片段被推到类型中
fn f1(name : T , other : T){ //T需要实现 FromForm 和上面表单一样
}
程序处理是错误的,错误来自一下来源:
捕捉器
catch
属性声明的register()
而不是 mount()
注册捕捉器可以接受零个、一个或两个参数。如果 捕捉器 接受一个参数,它必须是 &Request 类型。它需要两个,它们必须按顺序是 Status 和
&Request
类型。与路由一样,返回类型必须实现Responder
#[catch(404)]//捕捉404
fn f1(req: &Request) -> &str {
"刘的很,就是牛"
}
#[catch(default)]//默认捕捉器 , 如果没有其他捕捉器进行捕捉,那么默认捕捉器将会进行捕捉
fn f2(req: &Request) -> &str {
"刘的很,就是牛"
}
fn main(){
rocket::build().register("/", catchers![f1,f2]);//注册捕捉器
//register( 捕捉器范围 , 捕捉器)
}
Catcher API
功能 | 需要的实现 | 描述 |
---|---|---|
响应类型 | Responder (可派生) | #[get("/")] fn f() -> T {} |
单路径匹配 | FromParam | #[get("/")] |
多路径匹配 (段守卫 ) |
FromSegments | #[get("/ |
请求守卫 |
FromRequest | 就类似于注入那个 |
Cookie (请求守卫 ) |
CookieJar | |
Body数据(数据守卫 ) |
FromData | 需要通过data 属性来指明 |
JSON数据守卫 ) |
T 类型 接受实现 Deserialize(可派生) ,反馈实现 Serialize(可派生) | |
Form |
T 类型实现 FromForm (可派生) | 也可以用于匹配参数 “/?&” |
Strict |
默认宽松,可用在字段上 |