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