6 个版本

新版本 0.0.6 2024 年 8 月 16 日
0.0.5 2024 年 8 月 14 日
0.0.4 2024 年 7 月 20 日

#505 in HTTP 服务器

Download history 176/week @ 2024-07-05 144/week @ 2024-07-12 148/week @ 2024-07-19 23/week @ 2024-07-26 97/week @ 2024-08-09

290 每月下载量

MIT 许可证

38KB
500

Restify

Crates.io Docs.rs

Restify 是一个强大的 Rust 框架,旨在简化构建 Web 服务器的过程,同时支持多个 Web 框架(如 Axum、Actix、Rocket 等),并能无缝集成 OpenAPI 规范生成,以便轻松生成 API 文档。

特性

  • 多框架支持: 使用您首选的 Web 框架构建应用程序,目前支持 Axum,并计划集成 Actix 和 Rocket。
  • 声明式路由: 使用直观的宏定义路由和控制器,使代码更清晰易读。
  • 自动 OpenAPI 生成: Restify 根据您定义的路由和数据结构自动生成 OpenAPI 文档。
  • 模块化结构: 将应用程序组织成模块,以实现更好的可维护性和可扩展性。
  • 状态管理: 轻松管理共享应用程序状态。
  • 中间件支持: 享受所选 Web 框架的中间件功能。

路线图

  • Axum
  • Actix
  • Rocket
  • OpenAPI

安装

将以下内容添加到您的 Cargo.toml 文件中,选择您所需 Web 框架的功能

[dependencies]
restify = { version = "0.0.3", features = ["axum"] }

# Dependencies for your chosen web framework (example for Axum)
axum = "0.6"
tokio = { version = "1", features = ["full"] }

用法

1. 定义您的数据结构

// /todo/entities.rs

use serde::Serialize;

#[derive(Debug, Serialize, Clone)]
pub struct TodoEntity {
  pub id: String,
  pub name: String,
  pub done: bool,
}
// /todo/dto.rs

use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct CreateTodoDto {
  pub name: String,
}

#[derive(Debug, Deserialize)]
pub struct UpdateTodoDto {
  #[serde(default)]
  pub name: Option<String>,

  #[serde(default)]
  pub done: Option<bool>,
}

2. 创建您的服务

// src/todo/service.rs

use std::collections::HashMap;

use restify::prelude::*;
use uuid::Uuid;

use crate::app::AppState;

use super::{
  dto::{CreateTodoDto, UpdateTodoDto},
  entities::TodoEntity,
};

use axum::{extract::State, http::StatusCode, response::Response};

#[derive(Injectable)]
pub struct TodoService {
  #[from_request(via(State))]
  state: AppState,
}

impl TodoService {
  pub async fn get_all(&self) -> HashMap<String, TodoEntity> {
    self.state.store.lock().await.clone()
  }

  pub async fn get_one(&self, id: String) -> Option<TodoEntity> {
    self.state.store.lock().await.get(&id).cloned()
  }

  pub async fn create(&self, dto: CreateTodoDto) -> TodoEntity {
    let id = Uuid::new_v4().to_string();

    let todo = TodoEntity {
      id: id.clone(),
      name: dto.name,
      done: false,
    };

    self
      .state
      .store
      .lock()
      .await
      .insert(id.clone(), todo.clone());

    todo
  }

  pub async fn update(&self, id: String, dto: UpdateTodoDto) -> Result<TodoEntity, Response> {
    let mut store = self.state.store.lock().await;

    if let Some(todo) = store.get_mut(&id) {
      if let Some(name) = dto.name {
        todo.name = name;
      }

      if let Some(done) = dto.done {
        todo.done = done;
      }

      return Ok(todo.clone());
    }

    return Err(
      Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(().into())
        .unwrap(),
    );
  }

  pub async fn delete(&self, id: String) -> Result<TodoEntity, Response> {
    self.state.store.lock().await.remove(&id).ok_or_else(|| {
      Response::builder()
        .status(StatusCode::NOT_FOUND)
        .body(().into())
        .unwrap()
    })
  }
}

3. 创建您的控制器

// src/todo/controller.rs

use axum::{extract::Path, response::Response, Json};
use restify::prelude::*;
use tower_http::trace::TraceLayer;

use super::{
  dto::{CreateTodoDto, UpdateTodoDto},
  entities::TodoEntity,
  services::TodoService,
};

#[derive(Injectable)]
pub struct TodoController {
  service: TodoService,
}

#[controller("/todo", wrap = TraceLayer::new_for_http())]
impl TodoController {
  #[get]
  async fn get_all(self) -> Json<HashMap<String, TodoEntity>> {
    Json(self.service.get_all().await)
  }
  // ... other routes for creating, updating, and deleting todos
}

4. 定义您的模块

/// src/todo/mod.rs
use restify::Module;

#[derive(Module)]
#[module(controllers(TodoController))]
pub struct TodoModule;
// src/app.rs

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

use restify::prelude::*;
use tokio::sync::Mutex;
use tower_http::trace::TraceLayer;

use crate::todo::{entities::TodoEntity, TodoModule};

#[derive(Module)]
#[module(imports(TodoModule), controllers(AppController))]
pub struct AppModule;

pub struct AppController;

#[controller("/", wrap = TraceLayer::new_for_http())]
impl AppController {
  #[get]
  async fn up() -> &'static str {
    "UP!"
  }
}

#[derive(Clone, Default)]
pub struct AppState(Arc<AppStateInner>);

#[derive(Default)]
pub struct AppStateInner {
  pub store: Mutex<HashMap<String, TodoEntity>>,
}

impl Deref for AppState {
  type Target = AppStateInner;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

6. 如果您使用Axum并具有状态,可以创建一个restify.toml文件来定义状态路径,这样您就不必在每一个ModuleControllerInjectable中添加,如下所示:#[module(state(AppState))]

# restify.toml

state = "crate::app::AppState"

5. 创建您的应用程序(以Axum为例)

use app::{AppModule, AppState};

use restify::axum::IntoRouter; // Import specific to Axum

mod app;
mod todo;

#[tokio::main]
async fn main() {
  tracing_subscriber::fmt::init();

  let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
    .await
    .unwrap();

  axum::serve(
    listener,
    AppModule
      .into_router(&mut ())
      .with_state(AppState::default()),
  )
  .await
  .unwrap();
}

配置

Restify通过controllerModule宏提供配置选项。这些选项允许您指定

  • 路径: 控制器路由的基本路径。
  • 状态: 要注入控制器的共享应用程序状态类型(对于Axum)。
  • 包装: 应用到控制器路由的中间件层(使用您选择的框架的中间件机制)。

贡献

欢迎贡献!如果您想为Restify做出贡献,请按照以下步骤操作

  1. 分叉存储库。
  2. 为您的功能或错误修复创建一个新的分支。
  3. 进行更改并编写测试。
  4. 提交拉取请求。

许可证

本项目受[MIT许可证][license]许可。

致谢

  • Axum、Actix、Rocket: 支持的Web框架。
  • OpenAPI规范: 定义REST的标准

依赖项

~5–17MB
~193K SLoC