所有文章 > 如何集成API > Rust 与 Postgres 构建 CRUD API 全教程
Rust 与 Postgres 构建 CRUD API 全教程

Rust 与 Postgres 构建 CRUD API 全教程

编者按:本文于 2022 年 2 月 22 日更新,修复了与 Diesel 的连接问题,并进行了其他改进。

要继续本教程,你需要对 API 开发有基本了解,至少熟悉 CRUD(创建、读取、更新、删除)概念。本文将带你一步步学习如何使用 Rust 和 PostgreSQL 创建后端 API。


一. 项目要求

在开始编码之前,需要明确项目目标,并回顾完成演示所需的基本要求和假设。

我们将为一个示例员工管理应用程序创建完整的 CRUD API 端点。本项目是全新示例,旨在展示在 Rust 中编写 API 的基础构建块。

1. 需要了解的术语

本指南适合对 Rust 有基本理解的读者,以下术语需熟悉:

  • Spawn blocking:用于处理繁重或长时间任务(例如从数据库中检索大量记录)。通过生成新线程,避免异步函数阻塞。

二. Rust 项目设置及结构

首先,使用 Cargo 创建 Rust 项目:

cargo new employee

1. 项目结构

项目文件夹结构如下,可根据需要调整:

employee/
├─ src/
│ └─ employees/
│ ├─ mod.rs
│ ├─ model.rs
│ ├─ routes.rs
│ ├─ db.rs
│ └─ error_handlers.rs
│ └─ main.rs
├─ .env
└─ Cargo.toml

a. mod.rs 文件

用于管理 employees 目录中模块:

mod model;
mod routes;
pub use model::*;
pub use routes::init_routes;

b. Cargo.toml 文件

项目依赖项配置:

[package]
name = "employee"
version = "0.1.0"
authors = ["Ola John"]
edition = "2018"

[dependencies]
actix-web = "3.0"
actix-rt = "1.0"
chrono = { version = "0.4", features = ["serde"] }
dotenv = "0.11"
diesel = { version = "1.4", features = ["postgres", "r2d2", "uuid", "chrono"] }
diesel_migrations = "1.4"
env_logger = "0.6"
lazy_static = "1.4"
listenfd = "0.3"
serde = "1.0"
serde_json = "1.0"
r2d2 = "0.8"
uuid = { version = "0.6", features = ["serde", "v4"] }

三. 主程序设置

主程序包含关键 crate 和配置:

  • listenfd:检测文件变化并自动重启服务器
  • dotenv:管理环境变量
  • db::init():初始化数据库连接
  • employees::init_routes:注册所有 API 路由

actix-web 服务器设置示例:

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    db::init();
    let mut listenfd = ListenFd::from_env();
    let mut server = HttpServer::new(|| App::new().configure(employees::init_routes));
    server = match listenfd.take_tcp_listener(0)? {
        Some(listener) => server.listen(listener)?,
        None => {
            let host = env::var("HOST").expect("Please set host in .env");
            let port = env::var("PORT").expect("Please set port in .env");
            server.bind(format!("{}:{}", host, port))?
        }
    };
    server.run().await
}

四. 创建 API 端点

API 端点定义在 routes.rs 中。使用 serde 进行 JSON 序列化和反序列化。

#[get("/employees")]
async fn find_all() -> Result<HttpResponse, Error> {
    let employees = web::block(|| Employees::find_all()).await.unwrap();
    Ok(HttpResponse::Ok().json(employees))
}

#[get("/employees/{id}")]
async fn find(id: web::Path<i32>) -> Result<HttpResponse, Error> {
    let employee = Employees::find(id.into_inner())?;
    Ok(HttpResponse::Ok().json(employee))
}

#[post("/employees")]
async fn create(employee: web::Json<Employee>) -> Result<HttpResponse, Error> {
    let employee = Employees::create(employee.into_inner())?;
    Ok(HttpResponse::Ok().json(employee))
}

#[put("/employees/{id}")]
async fn update(
    id: web::Path<i32>,
    employee: web::Json<Employee>,
) -> Result<HttpResponse, Error> {
    let employee = Employees::update(id.into_inner(), employee.into_inner())?;
    Ok(HttpResponse::Ok().json(employee))
}

#[delete("/employees/{id}")]
async fn delete(id: web::Path<i32>) -> Result<HttpResponse, Error> {
    let deleted_employee = Employees::delete(id.into_inner())?;
    Ok(HttpResponse::Ok().json(json!({ "deleted": deleted_employee })))
}

pub fn init_routes(config: &mut web::ServiceConfig) {
    config.service(find_all);
    config.service(find);
    config.service(create);
    config.service(update);
    config.service(delete);
}

五. 配置 Postgres 数据库连接

db.rs 文件示例:

use crate::error_handler::CustomError;
use diesel::pg::PgConnection;
use diesel::r2d2::ConnectionManager;
use lazy_static::lazy_static;
use r2d2;
use std::env;

type Pool = r2d2::Pool<ConnectionManager<PgConnection>>;
pub type DbConnection = r2d2::PooledConnection<ConnectionManager<PgConnection>>;

embed_migrations!();

lazy_static! {
    static ref POOL: Pool = {
        let db_url = env::var("DATABASE_URL").expect("Database url not set");
        let manager = ConnectionManager::<PgConnection>::new(db_url);
        Pool::new(manager).expect("Failed to create db pool")
    };
}

pub fn init() {
    lazy_static::initialize(&POOL);
    let conn = connection().expect("Failed to get db connection");
    embedded_migrations::run(&conn).unwrap();
}

pub fn connection() -> Result<DbConnection, CustomError> {
    POOL.get()
        .map_err(|e| CustomError::new(500, format!("Failed getting db connection: {}", e)))
}

六. 使用 Diesel ORM

Diesel 简化数据库查询和对象映射。

#[derive(Serialize, Deserialize, AsChangeset, Insertable)]
#[table_name = "employees"]
pub struct Employee {
    pub first_name: String,
    pub last_name: String,
    pub department: String,
    pub salary: i32,
    pub age: i32,
}

#[derive(Serialize, Deserialize, Queryable)]
pub struct Employees {
    pub id: i32,
    pub first_name: String,
    pub last_name: String,
    pub department: String,
    pub salary: i32,
    pub age: i32,
}

七. 运行 Rust API 演示

启动项目:

cargo watch -x run

通过 API 端点,可以在数据库中执行 CRUD 操作:创建、读取、更新和删除员工记录。


八. 结论

通过本教程,你已经掌握了如何使用 Actix 在 Rust 中创建 API 端点,并处理 CRUD 操作,同时使用 Diesel 管理 Postgres 数据。

Rust 编译器确保高可靠性,成功编译几乎保证代码功能正确。这使开发过程更加高效、可维护。

原文链接: https://blog.logrocket.com/create-backend-api-with-rust-postgres/

#你可能也喜欢这些API文章!

我们有何不同?

API服务商零注册

多API并行试用

数据驱动选型,提升决策效率

查看全部API→
🔥

热门场景实测,选对API

#AI文本生成大模型API

对比大模型API的内容创意新颖性、情感共鸣力、商业转化潜力

25个渠道
一键对比试用API 限时免费

#AI深度推理大模型API

对比大模型API的逻辑推理准确性、分析深度、可视化建议合理性

10个渠道
一键对比试用API 限时免费