程式語言 - Rust - Web Server(CRUD)



參考資訊:
https://www.rust-lang.org/learn/get-started
https://www.readfog.com/a/1674123278515539968
http://bzz.wallizard.com:8081/share/books/RUST/Programming%20Rust%202nd%20Edition.pdf

產生樣板

$ cargo new hello
$ cd hello

Cargo.toml

[package]
name = "hello"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0"
anyhow = "1.0"
futures = "0.3"
actix-web = "1.0.8"
serde = { version = "1.0", features = [ "derive" ] }
tokio = { version = "1.12.0", features = [ "full" ] }
sqlx = { version = "0.5.7", features = [ "runtime-tokio-rustls", "sqlite" ] }

src/main.rs

use std::net::SocketAddr;
use sqlx::sqlite::SqlitePool;
use axum::extract::Extension;
use axum::{response::Html, routing::get, Router};

#[tokio::main]
async fn main() {
    let _pool = SqlitePool::connect("sqlite:test.db").await.expect("failed to connect sqlite");
    let app = Router::new()
        .route("/", get(get_index))
        .route("/create", axum::routing::post(post_create))
        .route("/read", axum::routing::post(post_read))
        .route("/update", axum::routing::post(post_update))
        .route("/delete", axum::routing::post(post_delete))
        .layer(Extension(_pool));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn get_index(Extension(_pool): Extension<SqlitePool>) -> Html<String> {
    Html(format!("
        <title>CRUD Test</title>
        <form action = \"/create\" method = \"post\"><button type = \"submit\">create</button></form>
        <form action = \"/read\"   method = \"post\"><button type = \"submit\">read</button></form>
        <form action = \"/update\" method = \"post\"><button type = \"submit\">update</button></form>
        <form action = \"/delete\" method = \"post\"><button type = \"submit\">delete</button></form>"))
}

async fn post_create(Extension(_pool): Extension<SqlitePool>) -> Html<String> {
    let user : &str = "test";
    let pass : &str = "1234";
    let _r = sqlx::query!("create table if not exists account(name text primary key, password text not null)").fetch_all(&_pool).await;
    let _r = sqlx::query!(r#"insert into account(name, password) values($1, $2)"#, user, pass).fetch_all(&_pool).await;
    Html(format!("complete !").to_string())
}

async fn post_read(Extension(_pool): Extension<SqlitePool>) -> Html<String> {
    let _r = sqlx::query!("select * from account").fetch_all(&_pool).await;
    Html(format!("{:?}", _r).to_string())
}

async fn post_update(Extension(_pool): Extension<SqlitePool>) -> Html<String> {
    let user : &str = "test";
    let pass : &str = "5678";
    let _r = sqlx::query!(r#"update account set password=$2 where name=$1"#, user, pass).fetch_all(&_pool).await;
    Html(format!("complete !").to_string())
}

async fn post_delete(Extension(_pool): Extension<SqlitePool>) -> Html<String> {
    let _r = sqlx::query!("drop table account").fetch_all(&_pool).await;
    Html(format!("complete !").to_string())
}

產生資料庫

$ export DATABASE_URL="sqlite:test.db"
$ sqlx migrate add test
$ vim migrations/xxx_test.sql
    -- Add migration script here
    CREATE TABLE account (
         name TEXT PRIMARY KEY,
         password TEXT NOT NULL
    );

$ sqlx db create
$ sqlx migrate run

執行

$ cargo run

開啟網頁並且輸入http://127.0.0.1:3000