Rust插件库Paperclip的使用:简化API文档生成与管理的Rust工具

Rust插件库Paperclip的使用:简化API文档生成与管理的Rust工具

Paperclip简介

Paperclip是一个为OpenAPI规范提供工具支持的Rust库。它提供以下功能:

  • 为高效、类型安全、编译时检查的HTTP API(服务器、客户端和CLI)生成Rust代码
  • 支持处理、验证和托管OpenAPI规范
  • 为规范和代码生成提供自定义选项

目前该项目仍在积极开发中,可能尚未准备好用于生产环境。

本地开发

  1. 克隆仓库(包含子模块):
git clone --recurse-submodules git@github.com:paperclip-rs/paperclip.git
  1. 确保已安装rustup
  2. 进入仓库目录:
cd paperclip
  1. 运行环境准备:
make prepare
  1. 构建项目并运行测试:
make

安装

全局安装二进制

cargo install paperclip

作为库安装

在项目目录中运行:

cargo add paperclip

或在Cargo.toml中添加:

paperclip = "0.9.5"

完整示例

以下是一个使用Paperclip生成API文档的完整示例:

use paperclip::v2::{
    models::{Api, Info},
    schema::Apiv2Schema,
};

fn main() {
    // 创建一个基本的OpenAPI文档结构
    let mut api = Api::default();
    
    // 设置API基本信息
    api.info = Info {
        version: "1.0.0".to_owned(),
        title: "示例API".to_owned(),
        description: Some("这是一个示例API文档".to_owned()),
        ..Default::default()
    };
    
    // 添加一个路径
    api.paths.insert(
        "/pets".to_owned(),
        paperclip::v2::models::PathItem {
            get: Some(paperclip::v2::models::Operation {
                responses: {
                    let mut map = std::collections::BTreeMap::new();
                    map.insert(
                        "200".to_owned(),
                        paperclip::v2::models::Response {
                            description: "成功获取宠物列表".to_owned(),
                            ..Default::default()
                        },
                    );
                    map
                },
                ..Default::default()
            }),
            ..Default::default()
        },
    );
    
    // 将API文档写入文件
    let api_json = serde_json::to_string_pretty(&api).unwrap();
    std::fs::write("openapi.json", api_json).expect("无法写入文件");
    
    println!("OpenAPI文档已生成到openapi.json");
}

常见问题

为什么使用原始Rust代码生成而不是利用过程宏进行编译时代码生成?

作者认为proc宏不是REST API的正确方式。我们需要能够看到生成的代码,以识别名称、字段、支持的方法等。使用proc宏时,你只能猜测。

编译时抛出的错误看起来不太有用。没有更好的方法吗?

目前作者没有想到更好的方法,欢迎提出新想法。

贡献指南

该项目欢迎各种贡献。如果您想贡献但不了解如何开始,或者需要与项目相关的帮助,可以发送电子邮件或加入Discord服务器。

许可证

项目采用以下许可证之一:

  • Apache License, Version 2.0
  • MIT license

1 回复

Rust插件库Paperclip的使用:简化API文档生成与管理的Rust工具

Paperclip是一个用于简化API文档生成与管理的Rust插件库,它可以帮助开发者自动生成和维护API文档,特别适合Rust web框架(如Actix-web、Rocket等)项目使用。

主要特性

  • 自动从Rust代码生成OpenAPI/Swagger规范
  • 与现有web框架无缝集成
  • 支持代码优先的API开发方式
  • 提供实时文档预览功能
  • 减少手动维护文档的工作量

安装方法

在Cargo.toml中添加依赖:

[dependencies]
paperclip = { version = "0.7", features = ["actix"] }  # 如果使用Actix-web
# 或
paperclip = { version = "0.7", features = ["v2"] }     # 对于OpenAPI v2支持

基本使用方法

1. 与Actix-web集成示例

use actix_web::{web, App, HttpServer, Responder};
use paperclip::actix::{
    api_v2_operation, web as apiv2_web, Apiv2Schema, OpenApiExt,
};

#[derive(Apiv2Schema)]
struct Pet {
    id: u64,
    name: String,
}

#[api_v2_operation]
async fn get_pets() -> impl Responder {
    // 实际业务逻辑
    web::Json(vec![
        Pet { id: 1, name: "Fido".into() },
        Pet { id: 2, name: "Mittens".into() },
    ])
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap_api()  // 启用Paperclip
            .with_json_spec_at("/api/spec")  // 指定API规范路径
            .with_swagger_ui_at("/docs")     // 指定Swagger UI路径
            .service(
                apiv2_web::resource("/pets")
                    .route(apiv2_web::get().to(get_pets))
            )
            .build()
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

2. 定义API模型和操作

use paperclip::actix::{Apiv2Schema, api_v2_operation};
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize, Apiv2Schema)]
struct User {
    id: u64,
    username: String,
    email: String,
}

#[api_v2_operation]
async fn create_user(user: web::Json<User>) -> impl Responder {
    // 创建用户的逻辑
    web::Json(user.into_inner())
}

3. 添加API文档描述

#[api_v2_operation(
    summary = "获取所有用户",
    description = "返回系统中注册的所有用户列表",
    tags = "Users"
)]
async fn get_users() -> impl Responder {
    // 实现逻辑
}

高级功能

1. 参数验证文档

#[api_v2_operation]
async fn search_users(
    query: web::Query极好的,我们来为Paperclip创建一个完整的示例demo,展示如何构建一个完整的API服务并生成文档。

### 完整示例demo

```rust
use actix_web::{web, App, HttpServer, Responder, HttpResponse};
use paperclip::actix::{
    api_v2_operation, web as apiv2_web, Apiv2Schema, OpenApiExt
};
use serde::{Serialize, Deserialize};

// 1. 定义数据模型
#[derive(Debug, Serialize, Deserialize, Apiv2Schema)]
struct User {
    id: u64,
    username: String,
    email: String,
}

#[derive(Debug, Serialize, Deserialize, Apiv2Schema)]
struct NewUser {
    username: String,
    email: String,
}

#[derive(Debug, Serialize, Apiv2Schema)]
struct ErrorResponse {
    message: String,
    code: u16,
}

// 2. 定义API处理函数
#[api_v2_operation(
    summary = "获取所有用户",
    description = "返回系统中注册的所有用户列表",
    tags = "Users"
)]
async fn get_users() -> impl Responder {
    // 模拟数据库查询
    let users = vec![
        User { id: 1, username: "user1".to_string(), email: "user1@example.com".to_string() },
        User { id: 2, username: "user2".to_string(), email: "user2@example.com".to_string() },
    ];
    web::Json(users)
}

#[api_v2_operation(
    summary = "创建新用户",
    description = "创建一个新的用户账户",
    tags = "Users",
    responses(
        (status = 201, description = "用户创建成功", body = User),
        (status = 400, description = "无效的请求数据", body = ErrorResponse)
    )
)]
async fn create_user(user: web::Json<NewUser>) -> Result<HttpResponse, Error> {
    // 验证逻辑
    if user.username.is_empty() || user.email.is_empty() {
        return Err(Error::BadRequest("用户名和邮箱不能为空".to_string()));
    }
    
    // 模拟保存到数据库
    let new_user = User {
        id: 3,
        username: user.username.clone(),
        email: user.email.clone(),
    };
    
    Ok(HttpResponse::Created().json(new_user))
}

#[api_v2_operation(
    summary = "获取单个用户",
    description = "根据用户ID获取用户详情",
    tags = "Users",
    responses(
        (status = 200, description = "成功获取用户信息", body = User),
        (status = 404, description = "用户未找到", body = ErrorResponse)
    )
)]
async fn get_user(user_id: web::Path<u64>) -> Result<web::Json<User>, Error> {
    // 模拟数据库查询
    if *user_id == 1 {
        Ok(web::Json(User {
            id: 1,
            username: "user1".to_string(),
            email: "user1@example.com".to_string(),
        }))
    } else {
        Err(Error::NotFound("用户不存在".to_string()))
    }
}

// 3. 自定义错误类型
#[derive(Debug)]
enum Error {
    BadRequest(String),
    NotFound(String),
}

impl actix_web::error::ResponseError for Error {
    fn error_response(&self) -> HttpResponse {
        match self {
            Error::BadRequest(msg) => HttpResponse::BadRequest().json(ErrorResponse {
                message: msg.clone(),
                code: 400,
            }),
            Error::NotFound(msg) => HttpResponse::NotFound().json(ErrorResponse {
                message: msg.clone(),
                code: 404,
            }),
        }
    }
}

// 4. 主函数
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            // 启用Paperclip
            .wrap_api()
            // 配置API文档路径
            .with_json_spec_at("/api/spec")
            .with_swagger_ui_at("/docs")
            // 自定义API信息
            .with_raw_json_spec(|app, spec| {
                spec.info.title = "用户管理API".to_string();
                spec.info.description = Some("一个简单的用户管理API示例".to_string());
                spec.info.version = "1.0".to_string();
                app
            })
            // 注册路由
            .service(
                apiv2_web::resource("/users")
                    .route(apiv2_web::get().to(get_users))
                    .route(apiv2_web::post().to(create_user))
            )
            .service(
                apiv2_web::resource("/users/{id}")
                    .route(apiv2_web::get().to(get_user))
            )
            .build()
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

示例说明

  1. 数据模型定义:使用Apiv2Schema派生宏标记结构体,使其能够被Paperclip识别并生成文档

  2. API处理函数

    • 使用api_v2_operation宏添加详细的API文档信息
    • 包含请求参数和响应体的类型信息
    • 定义错误响应格式
  3. 错误处理

    • 自定义错误类型实现ResponseError trait
    • 为每种错误情况定义对应的HTTP状态码和响应体
  4. 主函数配置

    • 启用Paperclip中间件
    • 配置API文档路径
    • 自定义API信息(标题、描述等)
    • 注册所有路由

启动服务后,可以通过以下方式访问API文档:

  • /api/spec - 查看OpenAPI规范
  • /docs - 查看交互式Swagger UI

这个完整示例展示了如何构建一个具有完整文档的用户管理API,包括:

  • 获取用户列表
  • 创建新用户
  • 获取单个用户详情
  • 错误处理
  • 详细的API文档生成
回到顶部