Rust HTTP API框架Dropshot的使用,Dropshot提供轻量级RESTful端点构建工具和OpenAPI集成
Rust HTTP API框架Dropshot的使用
Dropshot是一个通用但具有特定设计理念的Rust库,用于从Rust程序暴露REST API。它旨在保持简单和轻量级,并提供了对OpenAPI的一流支持,可以直接从代码生成精确的规范。它还支持一致的页面分页原语,包括在规范中表示它的OpenAPI扩展。
安装
在项目目录中运行以下Cargo命令:
cargo add dropshot
或者在Cargo.toml中添加:
dropshot = "0.16.2"
示例代码
下面是一个完整的Dropshot使用示例,展示了如何创建一个简单的REST API:
use dropshot::{endpoint, ApiDescription, HttpServer, HttpServerStarter, RequestContext};
use dropshot::endpoint::WebContext;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
// 定义一个简单的数据结构
#[derive(Serialize, Deserialize)]
struct Greeting {
message: String,
}
// 定义一个端点处理函数
#[endpoint {
method = GET,
path = "/greet/{name}",
}]
async fn greet(
rqctx: Arc<RequestContext<()>>,
name: String,
) -> Result<Greeting, dropshot::HttpError> {
Ok(Greeting {
message: format!("Hello, {}!", name),
})
}
#[tokio::main]
async fn main() -> Result<(), String> {
// 创建API描述
let mut api = ApiDescription::new();
api.register(greet).unwrap();
// 配置服务器
let server = HttpServerStarter::new(
&Default::default(),
api,
(),
&std::net::SocketAddr::from(([127, 0, 0, 1], 3000)),
)
.map_err(|e| e.to_string())?
.start();
// 运行服务器
server.await
}
这个示例展示了:
- 定义了一个简单的数据结构
Greeting
- 创建了一个端点处理函数
greet
,它接受一个路径参数name
- 设置了API描述和服务器配置
- 启动服务器监听3000端口
OpenAPI集成
Dropshot的一个主要特点是它可以直接从你的Rust代码生成OpenAPI规范。当你运行服务器时,它会自动提供一个/openapi.json
端点,其中包含你的API的完整OpenAPI规范。
分页支持
Dropshot提供了内置的分页支持。下面是使用分页的示例:
use dropshot::{endpoint, PaginationParams, ResultsPage};
use serde_json::Value;
#[endpoint {
method = GET,
path = "/items",
}]
async fn list_items(
rqctx: Arc<RequestContext<()>>,
pag_params: PaginationParams<(), ()>,
) -> Result<ResultsPage<Value>, dropshot::HttpError> {
let items = vec![
serde_json::json!({"id": 1, "name": "Item 1"}),
serde_json::json!({"id": 2, "name": "Item 2"}),
];
Ok(ResultsPage {
items,
next_page: None,
})
}
完整示例
下面是一个更完整的Dropshot API示例,包含多个端点和状态共享:
use dropshot::{endpoint, ApiDescription, HttpServer, HttpServerStarter, RequestContext};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
// 定义共享状态
#[derive(Default)]
struct AppState {
db: Mutex<HashMap<u64, String>>,
}
// 定义数据结构
#[derive(Serialize, Deserialize)]
struct Item {
id: u64,
name: String,
}
#[derive(Serialize, Deserialize)]
struct ItemInput {
name: String,
}
// 创建项目端点
#[endpoint {
method = POST,
path = "/items",
}]
async fn create_item(
rqctx: Arc<RequestContext<AppState>>,
body: ItemInput,
) -> Result<Item, dropshot::HttpError> {
let state = rqctx.context();
let mut db = state.db.lock().unwrap();
let id = db.len() as u64 + 1;
db.insert(id, body.name.clone());
Ok(Item { id, name: body.name })
}
// 获取项目列表端点
#[endpoint {
method = GET,
path = "/items",
}]
async fn list_items(
rqctx: Arc<RequestContext<AppState>>,
) -> Result<Vec<Item>, dropshot::HttpError> {
let state = rqctx.context();
let db = state.db.lock().unwrap();
let items = db.iter()
.map(|(&id, name)| Item { id, name: name.clone() })
.collect();
Ok(items)
}
// 获取单个项目端点
#[endpoint {
method = GET,
path = "/items/{id}",
}]
async fn get_item(
rqctx: Arc<RequestContext<AppState>>,
id: u64,
) -> Result<Item, dropshot::HttpError> {
let state = rqctx.context();
let db = state.db.lock().unwrap();
match db.get(&id) {
Some(name) => Ok(Item { id, name: name.clone() }),
None => Err(dropshot::HttpError::for_not_found(
None,
"Item not found".to_string(),
)),
}
}
#[tokio::main]
async fn main() -> Result<(), String> {
// 创建API描述
let mut api = ApiDescription::new();
api.register(create_item).unwrap();
api.register(list_items).unwrap();
api.register(get_item).unwrap();
// 创建共享状态
let shared_state = AppState::default();
// 配置服务器
let server = HttpServerStarter::new(
&Default::default(),
api,
shared_state,
&std::net::SocketAddr::from(([127, 0, 0, 1], 3000)),
)
.map_err(|e| e.to_string())?
.start();
// 运行服务器
server.await
}
这个完整示例展示了:
- 定义共享状态
AppState
用于存储数据 - 创建了三个端点用于:
- POST /items - 创建新项目
- GET /items - 获取项目列表
- GET /items/{id} - 获取单个项目
- 使用Mutex实现线程安全的状态共享
- 包含了错误处理逻辑
- 保持简单API设计的同时展示Dropshot的核心功能
Rust HTTP API框架Dropshot的使用指南
Dropshot是一个轻量级的Rust HTTP API框架,专注于构建RESTful端点和提供OpenAPI集成。它特别适合需要自动生成OpenAPI文档的项目。
主要特性
- 轻量级且易于使用的RESTful端点构建工具
- 自动生成OpenAPI 3.0文档
- 内置支持JSON请求/响应
- 与hyper集成,提供异步支持
- 类型安全的端点处理
基本使用方法
1. 添加依赖
首先在Cargo.toml
中添加dropshot依赖:
[dependencies]
dropshot = "0.8"
tokio = { version = "1.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
2. 创建基本API服务器
use dropshot::{ApiDescription, HttpServer, ConfigDropshot};
use dropshot::endpoint;
use dropshot::RequestContext;
use dropshot::HttpError;
use http::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize, Deserialize)]
struct Greeting {
message: String,
}
#[endpoint {
method = GET,
path = "/greet/{name}",
}]
async fn greet(
ctx: Arc<RequestContext>,
name: String,
) -> Result<Greeting, HttpError> {
Ok(Greeting {
message: format!("Hello, {}!", name),
})
}
#[tokio::main]
async fn main() -> Result<(), String> {
let mut api = ApiDescription::new();
api.register(greet).unwrap();
let server = HttpServer::new(
&ConfigDropshot::default(),
api,
Arc::new(()), // 共享状态
)
.await
.map_err(|e| e.to_string())?;
server.await
}
3. 启动服务器并测试
运行程序后,可以访问:
- 获取JSON响应
- 查看自动生成的OpenAPI文档
高级功能
请求体和参数处理
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[endpoint {
method = POST,
path = "/users",
}]
async fn create_user(
ctx: Arc<RequestContext>,
body: Json<CreateUser>,
) -> Result<Response<()>, HttpError> {
println!("Creating user: {} <{}>", body.name, body.email);
Ok(Response::builder()
.status(StatusCode::CREATED)
.body(())
.unwrap())
}
共享状态
struct AppState {
counter: Mutex<u32>,
}
#[endpoint {
method = GET,
path = "/counter",
}]
async fn get_counter(
ctx: Arc<RequestContext<AppState>>,
) -> Result<Json<u32>, HttpError> {
let counter = ctx.app_private.counter.lock().unwrap();
Ok(Json(*counter))
}
自定义错误处理
#[derive(Debug, Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
impl IntoResponse for ErrorMessage {
fn into_response(self) -> Response<BoxBody> {
let body = Json(self).into_response().into_body();
Response::builder()
.status(StatusCode::from_u16(self.code).unwrap()
.body(body)
.unwrap()
}
}
#[endpoint {
method = GET,
path = "/error",
}]
async fn error_endpoint() -> Result<String, ErrorMessage> {
Err(ErrorMessage {
code: 418,
message: "I'm a teapot".to_string(),
})
}
OpenAPI集成
Dropshot会自动为所有注册的端点生成OpenAPI文档。你还可以通过endpoint属性添加更多元数据:
#[endpoint {
method = GET,
path = "/user/{id}",
tags = ["user"],
summary = "Get user by ID",
description = "Returns a single user",
}]
async fn get_user(
ctx: Arc<RequestContext>,
id: u64,
) -> Result<Json<User>, HttpError> {
// 实现代码
}
完整示例代码
下面是一个完整的Dropshot API服务器示例,包含了基本路由、共享状态和错误处理:
use dropshot::{ApiDescription, HttpServer, ConfigDropshot, endpoint, HttpError, RequestContext};
use http::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
use tokio::sync::Mutex as AsyncMutex;
// 共享应用状态
#[derive(Default)]
struct AppState {
counter: AsyncMutex<u32>, // 使用异步互斥锁
users: Mutex<Vec<User>>, // 使用同步互斥锁(因为本例中users操作不涉及await)
}
// 用户数据结构
#[derive(Serialize, Deserialize, Clone)]
struct User {
id: u64,
name: String,
email: String,
}
// 错误响应
#[derive(Debug, Serialize)]
struct ApiError {
code: u16,
message: String,
}
// 获取计数器端点
#[endpoint {
method = GET,
path = "/counter",
}]
async fn get_counter(
ctx: Arc<RequestContext<AppState>>,
) -> Result<Json<u32>, HttpError> {
let counter = ctx.app_private.counter.lock().await;
Ok(Json(*counter))
}
// 增加计数器端点
#[endpoint {
method = POST,
path = "/counter/increment",
}]
async fn increment_counter(
ctx: Arc<RequestContext<AppState>>,
) -> Result<Json<u32>, HttpError> {
let mut counter = ctx.app_private.counter.lock().await;
*counter += 1;
Ok(Json(*counter))
}
// 创建用户端点
#[endpoint {
method = POST,
path = "/users",
}]
async fn create_user(
ctx: Arc<RequestContext<AppState>>,
body: Json<User>,
) -> Result<Response<()>, HttpError> {
let mut users = ctx.app_private.users.lock().unwrap();
users.push(body.into_inner());
Ok(Response::builder()
.status(StatusCode::CREATED)
.body(())
.unwrap())
}
// 获取所有用户端点
#[endpoint {
method = GET,
path = "/users",
}]
async fn get_users(
ctx: Arc<RequestContext<AppState>>,
) -> Result<Json<Vec<User>>, HttpError> {
let users = ctx.app_private.users.lock().unwrap();
Ok(Json(users.clone()))
}
// 自定义错误端点
#[endpoint {
method = GET,
path = "/error",
}]
async fn error_endpoint() -> Result<String, ApiError> {
Err(ApiError {
code: 418,
message: "I'm a teapot".to_string(),
})
}
#[tokio::main]
async fn main() -> Result<(), String> {
// 创建API描述
let mut api = ApiDescription::new();
api.register(get_counter).unwrap();
api.register(increment_counter).unwrap();
api.register(create_user).unwrap();
api.register(get_users).unwrap();
api.register(error_endpoint).unwrap();
// 初始化共享状态
let shared_state = Arc::new(AppState {
counter: AsyncMutex::new(0),
users: Mutex::new(Vec::new()),
});
// 配置并启动服务器
let server_config = ConfigDropshot {
bind_address: "127.0.0.1:8080".parse().unwrap(),
..Default::default()
};
let server = HttpServer::new(&server_config, api, shared_state)
.await
.map_err(|e| e.to_string())?;
println!("Server running on port 8080");
server.await
}
总结
Dropshot是一个简单但功能强大的Rust HTTP框架,特别适合需要良好文档化的RESTful API项目。它的自动OpenAPI集成可以显著减少API文档维护的工作量,同时保持类型安全和良好的性能。