Rust - Rocket框架 -基础入门

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(插件)

IDEARust插件并不友好,瞎报错,你没错也报错,而且并不支持Rocket框架的提示

开端

响应类型

响应类型必须实现 Responder(可派生)

#[get("/")]
fn index() -> T { /* ... */ }//T 需要实现Responder

&str,String,&[u8],Vec,File,(),Option,Result 都实现了 Responder

请求方法

Rust - Rocket框架 -基础入门_第1张图片

//例如
#[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

上面的jsontext是简写,简写文档

Body数据

想要获取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

注意!使用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的类型不匹配

File

你需要改一下配置文件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 
} 

Lenient 迫使解析是宽松的 ,虽然默认是宽松的,但是如果使用了Strict就是严格的了,可以再次通过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=abca[a]=abc

对于集合来说, 可以理解成 .key[key] 而非是解剖对象

比如 b.b.0=abcb.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=abcc.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 和上面表单一样
}

错误捕捉器

程序处理是错误的,错误来自一下来源:

  1. 一个失败的守卫
  2. 一个失败的响应者
  3. 路由失败

捕捉器

  1. 捕捉器仅在错误条件下被调用
  2. 捕捉器是用 catch 属性声明的
  3. 捕捉器使用 register() 而不是 mount() 注册
  4. 在调用捕获器之前,对 cookie 的任何修改都会被清除
  5. 错误捕捉器不能调用守卫
  6. 捕捉器不应无法产生响应
  7. 捕捉器的范围为路径前缀

捕捉器可以接受零个、一个或两个参数。如果 捕捉器 接受一个参数,它必须是 &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 严格表单 , Lenient 宽松表单 默认宽松,可用在字段上

你可能感兴趣的:(Rust,开发语言,后端,rust)