Crate axum_mongodb

source ·
Expand description

axum-mongodb

本库旨在为Axum项目提供一种更为简洁且优雅的MongoDB集成方案,其设计灵感来源于知名框架Nest.js。通过使用本库,开发者能够在Axum项目中实现对MongoDB数据库的高度简化及高效利用。

使用方式

1.安装依赖
cargo add axum-mongodb
2.在入口函数使用main属性宏

lib.rs

use anyhow::Result;
use axum::{response::IntoResponse, routing::get, Router};
use axum_mongodb::preload::*;
use mongodb::{options::ClientOptions, Client};
use tokio::net::TcpListener;
pub mod error;
mod todos;
use todos::Todo;

use crate::todos::todos_router;

// 在lib中使用,这样生成的结构体才能在整个项目中使用
#[axum_mongodb::main]
pub async fn start() -> Result<()> {
    let client_options =
        ClientOptions::parse("mongodb://mongodb:password@localhost:21045/admin").await?;
    let client = Client::with_options(client_options)?;
    let db = client.database("todo");

    // 定义State(关键代码)
    let mongodb_server = MongoDbServer::<Servers>::new(db).await;

    let app = Router::new()
        .route("/", get(hello_world))
        .merge(todos_router())
        // 注册State
        .with_state(mongodb_server);

    let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
    tracing::info!("listening on http://{}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();

    Ok(())
}

async fn hello_world() -> impl IntoResponse {
    "hello world"
}

main.rs

use anyhow::Result;
#[tokio::main]
async fn main() -> Result<()> {
    axum_example::start().await?;
    Ok(())
}
3.在结构体上使用Column Derive宏
use crate::Server;
use anyhow::Result;
use axum_mongodb::futures::TryStreamExt;
// 导入axum-mongodb
use axum_mongodb::preload::*;
use mongodb::{
    bson::{self, doc, oid::ObjectId},
    results::{DeleteResult, InsertOneResult, UpdateResult},
};
use serde::{Deserialize, Serialize};

#[derive(Debug, Column, Deserialize, Serialize, Clone)]
pub struct Todo {
    #[serde(
        serialize_with = "bson::serde_helpers::serialize_object_id_as_hex_string",
        rename = "_id"
    )]
    id: ObjectId,
    description: String,
    completed: bool,
    create_time: chrono::DateTime<chrono::Local>,
    update_time: chrono::DateTime<chrono::Local>,
}

// 实现相应方法,在handler中可以调用到
impl Server<Todo> {
    pub async fn create_todo(&self, description: String) -> Result<InsertOneResult> {
        Ok(self
            .insert_one(
                Todo {
                    id: ObjectId::new(),
                    description,
                    completed: false,
                    create_time: chrono::Local::now(),
                    update_time: chrono::Local::now(),
                },
                None,
            )
            .await?)
    }
		// ...
}
4.在axum handler中使用
use axum::{extract::Path, response::IntoResponse, Json};
use serde::Deserialize;

use super::Todo;
use crate::Server;

#[derive(Debug, Deserialize)]
pub struct TodoQuery {
    pub description: String,
    pub completed: Option<bool>,
}

pub async fn create_todo(
  	// 关键代码
    todo: Server<Todo>,
    Json(TodoQuery { description, .. }): Json<TodoQuery>,
) -> impl IntoResponse {
    let res = todo.create_todo(description).await.unwrap();
    Json(res)
}

// ...
5.注册路由
mod controller;
use controller::{create_todo, delete_todo, get_todo, get_todos, update_todo};
mod server;
use axum::{
    routing::{get, post},
    Router,
};
use axum_mongodb::MongoDbServer;
pub use server::Todo;

use crate::Servers;

pub fn todos_router() -> Router<MongoDbServer<Servers>> {
    Router::new()
        .route("/todos", post(create_todo).get(get_todos))
        .route(
            "/todos/:id",
            get(get_todo).put(update_todo).delete(delete_todo),
        )
}

完整示例代码axum-mongodb-example示例api文档

Modules

  • 重新导出常用的结构体和宏

Structs

  • MongoDbServer 一个结构体,用于存储数据库和集合,一个axum State,为其实现了FromRequestParts, 从而可以通过MongodbServer extract获取数据库和集合 可以使用crate::inject以及crate::preload::DBServers简化extract的使用

Attribute Macros

  • 用于axum handler,用于替换extract类型,简化操作
  • 最主要的宏,为Server实现[axum::extract::FromRequestParts]等

Derive Macros

  • Column Derive宏,用于收集结构体元信息,以及初始化mongodb的索引