IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Rust Rocket—HTTP服务端 -> 正文阅读

[网络协议]Rust Rocket—HTTP服务端

1. Hello, Rocket

首先在 Cargo.toml 中添加依赖:

[dependencies]
rocket = "0.5.0-rc.1"
#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};

#[get("/")]
fn index() -> &'static str {
    "Hello, Rocket!"
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![index])
}

2. 异步路由

路由处理器可以是 async 函数。

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::tokio::time::{sleep, Duration};

#[get("/delay")]
async fn delay() -> &'static str {
    sleep(Duration::from_secs(1)).await;
    "Hello, Rocket!"
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![delay])
}

注:mount 中指定的路径是基础路径,路由处理器对应的具体路径是“基础路径/路由处理器上指定的路径”。

3. 动态路径

通过 <变量名> 来指定。

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};

#[get("/hello/<name>")]
fn hello(name: &str) -> String {
    format!("Hello, {}!", name)
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![hello])
}

4. 查询参数

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};

#[get("/pictures?<id>&<width>&<height>")]
fn get_picture(id: &str, width: Option<usize>, height: Option<usize>) -> String {
    format!("id={}, width={}, height={}",
            id,
            match width {
                Some(v) => v,
                _ => 0,
            },
            match height {
                Some(v) => v,
                _ => 0,
            },
    )
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![get_picture])
}

请求:

http://127.0.0.1:8000/pictures?id=1&width=30

响应:

id=1, width=30, height=0

5. 处理 JSON

修改/添加依赖:

rocket = {version = "0.5.0-rc.1", features = ["json"]}
serde = { version = "1.0", features = ["derive"] }
#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::serde::{Serialize, Deserialize, json::Json};

#[derive(Serialize)]
#[derive(Deserialize)]
struct User {
    id: u32,
    name: String,
}

#[post("/new", data="<user>")]
fn new_user(user: Json<User>) -> Json<User> {
    Json(User {
        id: user.id,
        name: String::from(&user.name),
    })
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/user", routes![new_user])
}

6. 处理表单

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::form::Form;

#[derive(FromForm)]
struct User {
    id: u32,
    name: String,
}

#[post("/new", data="<user>")]
fn new_user(user: Form<User>) -> String {
    format!("id = {}, name = {}", user.id, user.name)
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/user", routes![new_user])
}

7. 上传文件

Rocket 会先将文件保存至临时目录(可配置)。

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::fs::TempFile;
use rocket::form::Form;

#[derive(FromForm)]
struct Upload<'f> {
    file: TempFile<'f>
}

#[post("/upload", data="<form>")]
async fn upload(mut form: Form<Upload<'_>>) {
    let path = r"E:\tmp\tmp.txt";
    println!("saved to: {:?}", form.file.path());

    form.file.copy_to(path).await.unwrap();
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![upload])
}

8. 下载文件

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::tokio::fs::File;
use rocket::http::ContentType;
use rocket::response::content;

#[get("/download")]
async fn download() -> content::Custom<File> {
    let path = r"E:\tmp\tmp.txt";
    content::Custom(ContentType::Binary, File::open(path).await.unwrap())
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build().mount("/", routes![download])
}

或者使用:

#[get("/download")]
async fn download() -> File {
    let path = r"E:\tmp\tmp.zip";
    File::open(path).await.unwrap()
}

9. 中间件

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::{Request, Data, Response};
use rocket::fairing::{Fairing, Info, Kind};
use rocket::http::{Method, ContentType, Status};
use std::io::Cursor;
use std::sync::atomic::{AtomicUsize, Ordering};

struct Counter {
    get: AtomicUsize,
    post: AtomicUsize,
}

impl Counter {
    fn new() -> Self {
        Counter {
            get: AtomicUsize::new(0),
            post: AtomicUsize::new(0),
        }
    }
}

#[rocket::async_trait]
impl Fairing for Counter {
    fn info(&self) -> Info {
        Info {
            name: "GET/POST Counter",
            kind: Kind::Request | Kind::Response
        }
    }

    async fn on_request(&self, request: &mut Request<'_>, _: &mut Data<'_>) {
        match request.method() {
            Method::Get => self.get.fetch_add(1, Ordering::Relaxed),
            Method::Post => self.post.fetch_add(1, Ordering::Relaxed),
            _ => return
        };
    }

    async fn on_response<'r>(&self, request: &'r Request<'_>, response: &mut Response<'r>) {
        if response.status() != Status::NotFound {
            return
        }

        if request.method() == Method::Get && request.uri().path() == "/counts" {
            let get_count = self.get.load(Ordering::Relaxed);
            let post_count = self.post.load(Ordering::Relaxed);
            let body = format!("Get: {}\nPost: {}", get_count, post_count);

            response.set_status(Status::Ok);
            response.set_header(ContentType::Plain);
            response.set_sized_body(body.len(), Cursor::new(body));
        }
    }
}

#[get("/")]
fn index() -> &'static str {
    "Hello, Rocket!"
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build()
        .attach(Counter::new())
        .mount("/", routes![index])
}
  • on_request:处理请求前执行;
  • on_response:发回响应前执行。

10. 请求守卫

在调用请求处理器之前执行,可以用来验证请求的合法性。

#[macro_use] extern crate rocket;
use rocket::{Build, Rocket};
use rocket::request::{FromRequest, Outcome};
use rocket::Request;
use rocket::http::Status;

struct User {
    id: u32,
}

struct Admin {
    id: u32,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, ()> {
        match request.headers().get_one("role") {
            Some("admin") | Some("user") => Outcome::Success(User{id: 1}),
            _ => Outcome::Failure((Status::Forbidden, ())),
        }
    }
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for Admin {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Admin, ()> {
        match request.headers().get_one("role") {
            Some("admin") => Outcome::Success(Admin {id: 0}),
            Some(_) => Outcome::Forward(()),
            _ => Outcome::Failure((Status::Forbidden, ())),
        }
    }
}

#[get("/dashboard")]
fn admin_dashboard(admin: Admin) -> String {
    format!("Admin: {}", admin.id)
}

#[get("/dashboard", rank=2)]
fn user_dashboard(user: User) -> String {
    format!("User: {}", user.id)
}

#[launch]
fn rocket() -> Rocket<Build> {
    rocket::build()
        .mount("/", routes![admin_dashboard, user_dashboard])
}
  • 请求守卫的返回值:

    • Success(S):验证成功,S 对应的值会成为请求处理器相应的参数。
    • Failure(Status, E):验证失败,直接返回响应。
    • Forward:转发给下一个匹配的请求处理器,如,以 User 身份请求 /dashboard 时,此处先尝试 admin_dashboard(转发),然后尝试 user_dashboard
  • 可以有多个请求守卫(遵从短路逻辑),如,此处 a, b, c 均是请求守卫(没有出现在 get 属性中)。

    #[get("/<param>")]
    fn index(param: isize, a: A, b: B, c: C) { /* ... */ }
    

11. 自定义配置

创建 Rocket.toml 文件:(Rocket 会在当前目录中查找此文件,找不到的话就到父目录中继续找,不断往上搜索)

[default]
address = "127.0.0.1"
port = 8000
workers = 16
keep_alive = 5
ident = "Rocket"
log_level = "normal"
temp_dir = "/tmp"
cli_colors = true
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="

[default.limits]
forms = "64 kB"
json = "1 MiB"
msgpack = "2 MiB"
"file/jpg" = "5 MiB"

[default.tls]
certs = "path/to/cert-chain.pem"
key = "path/to/key.pem"

[default.shutdown]
ctrlc = true
signals = ["term", "hup"]
grace = 5
mercy = 5

[release]
port = 9999
secret_key = "hPRYyVRiMyxpw5sBB1XeCMN1kFsDCqKvBi2QJxBVHQk="

每个参数的具体含义见 https://api.rocket.rs/v0.5-rc/rocket/struct.Config.html

注:如果使用 TLS,则需要修改依赖

[dependencies]
rocket = { version = "0.5.0-rc.1", features = ["tls"] }
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-10-11 17:52:51  更:2021-10-11 17:53:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年6日历 -2024/6/29 17:59:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码