iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Software Development

Rust的多方面運用系列 第 26

[Day26] 用 Rocket 做一個圖書館門禁後端 (Part 3)

今天就要打疫苗了 然後我現在還沒睡 掰了
明天沒看到我的文就代表真的掰了 QQ

好 今天準備的內容比較多一點
主要是前天看到學長在幫學校弄一個圖書館的登入系統
我想說能不能用 Rust Rocket Rebuild 出一個
當然 可能有部份內容不一樣 因為我根本沒看過他的 Code 想必各位讀者也不會看過
今天的 Code 頗多的
明天不太知道要講啥 我去問學長他還有什麼功能了 如果有問到應該就是繼續這個東東吧


那這篇的內容是使用 Rocket + Serde + SQLx + Chrono
閱讀前請先回去看之前的 SQLX教學配置環境 就是創造 .db檔之類的
以下是 Cargo.toml 檔

[dependencies]
chrono = "0.4.19"
rocket = { version = "0.5.0-rc.1", features = ["json"] }
serde = "1.0.130"
sqlx = { version = "0.5.7", features = [ "runtime-tokio-rustls", "sqlite" ] }
tokio = { version = "1.12.0", features = ["full"] }
anyhow = "1.0"
futures = "0.3"

然後我現在才發現 toml 會有顏色顯示

新增資料

那這邊的部份會需要兩個 Struct

#[derive(Serialize, Deserialize)]
pub struct User {
    username: String,
    student_id: String,
    pos: i32,
}

#[derive(Serialize, Deserialize)]
pub struct Input {
    username: String,
    student_id: String,
    pos: i32,
    time: String,
}

上面是 POST 資料進去我們的後端所用的 Struct
而下面是回傳以及使用資料庫的 Struct
那直接看我們 POST 的 Code

#[post("/get_in", format = "json", data = "<user>")]
async fn inside(user: Json<User>) -> Status {
    let username = user.username.clone();
    let student_id = user.student_id.clone();
    let pos = user.pos.clone();
    let time= Local::now().to_string();
    sql::add_data(Input {
        username,
        student_id,
        pos,
        time,
    }).await;
    Status::Accepted
}

那可以看到說就跟昨天的內容很像
也就是引入內容然後 clone
其實也可以在傳入那邊去 clone 這樣會比較簡潔
而 time 那邊我使用 chrono 裡面的函數,具體用法我沒有到很清楚,我只會一些用法
要注意的地方是 因為有使用 await 所以函數要加 async
那麼切到 SQL 的部份

pub async fn add_data(data: Input) -> anyhow::Result<()> {
    let pool = SqlitePool::connect(&env::var("DATABASE_URL")?).await?;
    let mut conn = pool.acquire().await?;
    let add = sqlx::query!(
        r#"INSERT INTO student (name, student_id, pos, time) VALUES ($1, $2, $3, $4)"#, data.username, data.student_id, data.pos, data.time)
        .execute(&mut conn).await?;
    Ok(())
}

可以看到說跟之前講 SQLX 的時候有些差別
就是不會傳入 pool 也就是 SqlitePool
最後回傳 OK

查詢所有資料

#[get("/all_data")]
async fn search() -> Json<Vec<Input>>{
    let result = sql::list_data().await;
    Json(result.unwrap())
}

這邊可以看到十分的簡短
所以我們直接切到 SQL 的部份

pub async fn list_data() -> anyhow::Result<(Vec<crate::Input>)> {
    let pool = SqlitePool::connect(&env::var("DATABASE_URL")?).await?;
    let result = sqlx::query!(r#"
        SELECT name, student_id, pos, time
        FROM student
        ORDER BY name
    "#).fetch_all(&pool).await?;
    let mut ret: Vec<crate::Input> = Vec::new();
    for i in result {
        ret.push(
            crate::Input {
                username: i.name.unwrap().clone(),
                student_id: i.student_id.clone(),
                pos: i.pos.parse::<i32>().unwrap().clone(),
                time: i.time.clone(),
            }
        );
    }
    Ok(ret)
}

那這邊這個 SQL 用法是照官方文檔寫的 實際上也可行
下面的部份就是去將資料塞到我們最後要回傳的 Code
而上面是 Result 的部份
所以也可以看到上面 search 的函數中最後回傳是有 unwrap 的
至於下面 pos 這邊我記得好像有講過 總之就是一個能夠弄成 atoi 的方法


那既然解釋完 Code 就來看最後成品吧!


傳入資料是這兩筆

傳入後會看到後端
左邊是後端 右邊是 POST 內容

最後可以看到資料

今天先這樣 明天打 BNT 怕


上一篇
[Day25] Rocket 序列化和反序列化以及 POST (Part 2)
下一篇
[Day 27] Bevy 遊戲引擎 (Part 1)
系列文
Rust的多方面運用30

尚未有邦友留言

立即登入留言