Rust Rocket簡單入門

二次元攻城狮發表於2024-03-19

目錄
  • 簡介
  • hello world
  • 常用功能
    • 動態路徑
    • 多個片段(segments)
    • 靜態檔案伺服器
  • 簡單WebAPI示例
    • 新增依賴
    • 實現介面
    • 介面測試
  • 參考連結

簡介

Rust中最知名的兩個web框架要數RocketActix了,Rocket更注重易用性,Actix則更注重效能。這裡只是瞭解一下Rust下的WebAPI開發流程,就學一下最簡單的 Rocket。

Rocket 是一個用於 Rust 的非同步 Web 框架,專注於可用性、安全性、可擴充套件性和速度:
github:https://github.com/rwf2/Rocket/tree/v0.5
官網:https://rocket.rs

hello world

需要最新版本的 Rust 來執行 Rocket 應用程式,執行以下命令確保安裝了最新的工具鏈:

rustup default stable

建立一個新的基於二進位制的 Cargo 專案並切換到新目錄:

cargo new hello-rocket --bin
cd hello-rocket

執行以下命令,新增 Rocket 依賴項:

cargo add rocket

在 src/main.rs 檔案中新增以下程式碼:

#[macro_use] extern crate rocket;

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

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

上面hello world示例沒有main函式,main函式由launch宏生成,可以透過原始碼看出:

pub fn launch(args: TokenStream, input: TokenStream) -> TokenStream {
    emit!(attribute::entry::launch_attribute(args, input))
}
//...
async_entry!(launch_attribute, launch::Launch, quote!(fn main() {}));

執行程式,訪問 http://localhost:8000 以檢視應用,VS終端輸出如下:
image

程式帶的有彩色輸出,如果在資料夾手動開啟後沒有彩色輸出,說明系統不支援ANSI轉義序列。

常用功能

動態路徑

動態路徑比較常見的場景是動態id場景,可以傳N個動態型別即動態路徑有多層,只要這個型別實現了FromParam

//訪問連結示例:http://localhost:8000/hello/張三/25/true
#[get("/hello/<name>/<age>/<is_male>")]
fn hello(name: &str, age: u8, is_male: bool) -> String {
    if is_male {
        format!("姓名 {} ,年齡 {}, 性別 男!", name, age)
    } else {
        format!("姓名 {} ,年齡 {}, 性別 女!", name, age)
    }
}

這個路由會匹配所有/hello/為基礎路徑的路由,然後將它匹配到的動態路徑作為引數傳遞給處理器,Rocket預設給標準庫裡的一些常見型別以及Rocket自身的一些特殊型別實現了FromParam trait。

多個片段(segments)

可以透過<param..>的方式來匹配多個動態路徑,這種型別的引數一般被叫做分段防護裝置(segments guards),都必須先實現FromSegments這個trait。

use std::path::PathBuf;

//訪問連結示例:http://localhost:8000/page/foo/bar
#[get("/page/<path..>")]
fn get_page(path: PathBuf) -> String {
    let mut output = String::new();
    for part in path.iter() {
        let part_str = part.to_string_lossy();
        println!("路徑引數: {}", part_str);
        output.push_str(&format!("路徑引數: {}\n", part_str));
    }
    output
}

PathBuf實現了FromSegments這個trait,所以不用擔心/page或者/page//導致的解析失敗,也不用擔心路徑遍歷攻擊(path traversal attacks)。

靜態檔案伺服器

基於 分段防護裝置(segments guards),可以簡單的實現一個安全的靜態檔案伺服器:

use std::path::{Path, PathBuf};
use rocket::fs::NamedFile;

#[get("public/<file..>")]
async fn files(file: PathBuf) -> Option<NamedFile> {
    NamedFile::open(Path::new("static/").join(file)).await.ok()
}

也可以使用 FileServer,只需一行程式碼即可:

//引入FileServer結構體
use rocket::fs::FileServer;

//將/public作為URI字首,並將static/作為檔案路徑
rocket.mount("/public", FileServer::from("static/"))

在專案根目錄下建立一個名為static的資料夾,並將靜態檔案 example.txt 放在其中,透過以下uri訪問檔案:

http://localhost:8000/public/example.txt

在釋出專案時,可以將靜態資料夾放在與可執行檔案相同的目錄中,或者根據部署需求將其放在其他位置。

簡單WebAPI示例

下面使用Rocket實現一個簡單的WebAPI,這裡的示例只實現Post方法,不涉及JWT鑑權。

新增依賴

執行以下命令新增 serde 依賴:

cargo add serde --features "derive"

再執行一遍以下命令,開啟 json 功能標誌:

cargo add rocket --features "json"

實現介面

在 src/main.rs 檔案中實現以下程式碼:

#[macro_use] extern crate rocket;
use rocket::serde::{Deserialize, Serialize,json::Json};

#[derive(Debug, Deserialize)]
#[serde(crate = "rocket::serde")]
struct TaskRequest {
    description: String,
    complete: bool
}

#[derive(Debug, Serialize)]
#[serde(crate = "rocket::serde")]
struct TaskResponse {
    description: String,
    complete: bool
}

#[post("/todo", data = "<task>")]
fn my_function(task: Json<TaskRequest>) -> Json<TaskResponse> {
    // 處理接收到的任務
    println!("Received task: {:?}", task);

    // 返回處理後的任務
    Json(TaskResponse {
        description: task.description.clone(),
        complete: task.complete,
    })
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![my_function])
}

介面測試

使用 curl 測試一下介面,在cmd中執行以下命令:

curl -X POST -H "Content-Type: application/json" -d "{\"description\":\"Task 1\",\"complete\":true}" http://localhost:8000/todo

測試結果:
image

參考連結

  • 簡單探索Rust Web開發
  • rust基礎學習——web框架Rocket簡單入門
  • Rocket 指導 v0.5 開始
  • cmd輸出彩色字型(win10 cmd控制檯支援ANSI轉義序列)