6 个版本
新版本 0.0.6 | 2024 年 8 月 16 日 |
---|---|
0.0.5 | 2024 年 8 月 14 日 |
0.0.4 | 2024 年 7 月 20 日 |
#505 in HTTP 服务器
290 每月下载量
38KB
500 行
Restify
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文件来定义状态路径,这样您就不必在每一个Module
、Controller
或Injectable
中添加,如下所示:#[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通过controller
和Module
宏提供配置选项。这些选项允许您指定
- 路径: 控制器路由的基本路径。
- 状态: 要注入控制器的共享应用程序状态类型(对于Axum)。
- 包装: 应用到控制器路由的中间件层(使用您选择的框架的中间件机制)。
贡献
欢迎贡献!如果您想为Restify做出贡献,请按照以下步骤操作
- 分叉存储库。
- 为您的功能或错误修复创建一个新的分支。
- 进行更改并编写测试。
- 提交拉取请求。
许可证
本项目受[MIT许可证][license]许可。
致谢
- Axum、Actix、Rocket: 支持的Web框架。
- OpenAPI规范: 定义REST的标准
依赖项
~5–17MB
~193K SLoC