#web-framework #axum #web #framework #http

nidrs

Nidrs is a web framework based on axum and tokio

3 releases

0.0.3 Apr 4, 2024
0.0.2 Apr 1, 2024
0.0.1 Mar 30, 2024

#224 in HTTP server

Download history 419/week @ 2024-03-29 109/week @ 2024-04-05

528 downloads per month

MIT license

23KB
166 lines

Discord

Nidrs

致敬 Nestjs 框架,Nidrs 是参考 Nestjs 思想的 Rust 企业级模块化开发框架,同时基于 Axum 进行开发和设计。

Nidrs 提供了一个即插即用的应用程序架构,使开发人员和团队能够轻松创建高度可测试、可扩展、松散耦合且易于维护的应用程序。

Nestjs 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。

Focus

  • 模块化封装 v0.0.1
    • 静态模块注册 v0.0.1
    • 可配置的模块注册 v0.0.2
  • 依赖自动注入
    • service 自动注入 v0.0.1
    • 动态 service 注入 v0.0.3
    • service 作用域(全局)v0.0.1
    • service 作用域(模块)
    • service 实例域(单例)v0.0.1
    • service 实例域(请求级)
    • service 实例域(注入级)
  • 分层架构
    • Controller 层 v0.0.1
    • Service 层 v0.0.1
    • Model 层
  • 模块生命周期钩子
    • on_module_init v0.0.2
    • on_module_destroy
    • on_application_bootstrap
    • on_application_shutdown
  • 请求响应拦截器 v0.0.4
  • 请求参数校验
  • 基于请求参数校验的 Mock 服务
  • 统一返回类型
  • 错误封装和处理
  • 自动 OpenAPI
  • 模块测试
  • CLI 命令
  • 完整的文档和例子

Example

example/src/app/controller.rs

use std::{collections::HashMap, sync::Arc};

use axum::{extract::{Query, State}, Json};
use nidrs::{Inject, StateCtx};
use nidrs_macro::{controller, get, post};

use super::service::AppService;

#[controller("/app")]
#[derive(Debug, Default)]
pub struct AppController {
    app_service: Inject<AppService>,
}

impl AppController {
    #[get("/hello")]
    pub async fn get_hello_world(&self, State(state): State<StateCtx>, Query(q): Query<HashMap<String, String>>) -> String {
        println!("Query {:?}", q);
        self.app_service.get_hello_world()
    }
}

example/src/app/service.rs

use nidrs::Inject;
use nidrs_macro::{injectable, on_module_init};
use crate::user::service::UserService;

#[injectable()]
#[derive(Clone, Debug, Default)]
pub struct AppService{
    user_service: Inject<UserService>
}

impl AppService {
    pub fn get_hello_world(&self) -> String {
        self.user_service.get_hello_world()
    }

    pub fn get_hello_world2(&self) -> String {
        "Hello, nidrs2xx333!".to_string()
    }
}

example/src/app/mod.rs

use nidrs_macro::module;

pub mod controller;
pub mod service;

use controller::AppController;
use service::AppService;
use crate::user::UserModule;
use crate::conf::ConfModule;
use crate::conf::ConfOptions;

#[module({
    imports = [
        ConfModule::for_root(ConfOptions{
            log_level: "info".to_string(),
        }),
        UserModule,
    ];
    controllers = [AppController];
    services = [AppService];
})]
#[derive(Clone, Debug, Default)]
pub struct AppModule;

运行例子:

git clone https://github.com/nidrs/nidrs.git
cd nidrs/example
cargo run

运行日志:

[nidrs] Registering module AppModule.
[nidrs] Registering controller AppController.
[nidrs] Registering router 'GET /app/hello'.
[nidrs] Registering router 'POST /app/hello'.
[nidrs] Registering service AppService.
[nidrs] Registering dyn service ConfOptions.
[nidrs] Registering module ConfModule.
[nidrs] Registering service ConfService.
[nidrs] Injecting ConfService.
[nidrs] Triggering event on_module_init for ConfService.
ConfService initialized with log_level: ConfOptions { log_level: "info" }
[nidrs] Registering module UserModule.
[nidrs] Registering controller UserController.
[nidrs] Registering router 'GET /user/hello'.
[nidrs] Registering service UserService.
[nidrs] Injecting UserService.
[nidrs] Injecting UserController.
[nidrs] Injecting AppService.
[nidrs] Injecting AppController.
[nidrs] Listening on 0.0.0.0:3000

Design

整个框架的目标是提高模块复用和极致的开发便携。

框架整体是通过宏来自动收集依赖关系,并且生成服务注册、依赖注入、路由注册的相关代码。

框架初始化大概可以分为两大阶段:

1. 模块注册
   1. 路由注册
   2. 服务注册
   3. 模块注册
2. 依赖注入

框架在初始化的时候会创建一个核心的对象:

#[derive(Debug, Clone)]
pub struct ModuleCtx{
    pub services: Arc<Mutex<HashMap<String, Box<dyn Any>>>>,
    pub controllers: Arc<Mutex<HashMap<String, Box<dyn Any>>>>,
    pub routers: Arc<Mutex<Vec<axum::Router<StateCtx>>>>
}

而服务整个注册的行为就是给 ctx.services.insert("AppService", AppService) 插入创建的实例。

而依赖注入会将标记为 Inject 的属性找出来,从 ctx.services map 上获取实例化后的 service 赋值给对应的属性,所以目前所有的服务都是单例。

具体想看宏生成的代码可以执行 cd nidrs/example && cargo expand 即可。

About

整个框架目前处于早期阶段,0.x.x 都处于测试版本,正式稳定版本从 1.0 开始,不过如果你只是单纯的想找一个 axum 类的高层框架,而不需要后面的功能也可以尝试一下 最后如果有感兴趣的同学想要贡献和开发也可以加入下面的 Discord 一起来为 rust 世界添砖加瓦。

欢迎加入 Discord ,微信群一起讨论交流

微信群

Dependencies

~9–21MB
~260K SLoC