Rust Web 開發實戰 -後端--4 數據庫存取和錯誤處理

上一篇文章《Rust Web 開發實戰 (後端)-3 應用配置和設置數據庫工具》,我們安裝了數據庫工具 Dbmate 及創建了用戶表。在這一篇中,我們終於可以在應用中進行數據庫存取了。

數據庫存取

SQLx 是令人驚訝的,它是一個純異步的 Rust SQL crate,具有編譯時檢查查詢功能,沒有 DSL。支持 PostgreSQL、MySQL、SQLite、MSSQL 等。

將以下內容添加到你的 app/Cargo.toml 中:

sqlx = { version = "0", default-features = false,  features = [ "runtime-tokio-rustls", "postgres", "macros", "chrono" ] }

將以下代碼添加到 app/src/models/user.rs 的文件中:

use sqlx::{Error, PgPool};
pub struct User {
    pub id: i32,
    pub email: String,
}
impl User {
    pub async fn get_users(pool: &PgPool, user_id: u32) -> Result<Vec<User>, Error> {
        Ok(sqlx::query_as!(
            User,
            "
                SELECT 
                    id, email
                FROM 
                    users
                WHERE
                    id < $1
            ",
            user_id as i32
        )
        .fetch_all(pool)
        .await?)
    }
}

你還需要創建一個名爲 app/src/models/mod.rs 的文件,並添加以下內容:

pub mod user;

創建一個名爲 models 的文件夾,併爲將要使用 SQLx 操作的每個 entity 添加一個文件。

.
├── .devcontainer/
│   └── ...
├── app
│   ├── src/
│   │   ├── models/
│   │   │   ├── user.rs
│   │   │   └── mod.rs
│   │   ├── main.rs
│   │   └── config.rs
├── db/
│   └── ...
├── Cargo.toml
└── Cargo.lock

更新 app/src/ main.rs:

mod config;
mod error;
mod models;
use axum::extract::Extension;
use axum::{response::Html, routing::get, Router};
use sqlx::PgPool;
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
    let config = config::Config::new();
    let db_pool = PgPool::connect(&config.database_url)
        .await
        .expect("Problem connecting to the database");
    // build our application with a route
    let app = Router::new()
        .route("/", get(handler))
        .layer(Extension(db_pool))
        .layer(Extension(config));
    // run it
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}
async fn handler(Extension(pool): Extension<PgPool>) -> Result<Html<String>, error::CustomError> {
    let users = models::user::User::get_users(&pool, 10).await?;
    let html = format!("<h1>Hello, World! We Have {} Users</h1>", users.len());
    Ok(Html(html))
}

這裏需要注意的主要事情是,我們的處理程序會神奇地通過框架自動傳入數據庫連接池。

我們還需要爲應用程序進行統一錯誤處理。創建一個名爲 app/src/error.rs 的文件:

use axum::response::{IntoResponse, Response};
use axum::http::StatusCode;
#[derive(Debug)]
pub enum CustomError {
    Database(String),
}
// So that errors get printed to the browser?
impl IntoResponse for CustomError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            CustomError::Database(message) => (StatusCode::UNPROCESSABLE_ENTITY, message),
        };
        format!("status = {}, message = {}", status, error_message).into_response()
    }
}
// Any errors from sqlx get converted to CustomError
impl From<sqlx::Error> for CustomError {
    fn from(err: sqlx::Error) -> CustomError {
        CustomError::Database(err.to_string())
    }
}

執行 cargo run,就可以在瀏覽器中輸入 http://localhost:3000 訪問應用程序:

本文翻譯自:

https://cloak.software/blog/rust-on-nails/#development-environment-as-code

coding 到燈火闌珊 專注於技術分享,包括 Rust、Golang、分佈式架構、雲原生等。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/U7vN1q0bXtSSNUDWge7pjQ