Rust插件库Okapi的使用:高性能API开发与扩展工具库

Okapi

Okapi: Download API Docs

Rocket-Okapi: Download API Docs

unsafe forbidden

为Rust/Rocket项目自动生成OpenAPI(又称Swagger)文档。

不再有过时的文档。Okapi会在设置服务器时为您生成文档。它结合使用Rust文档注释和编程逻辑来记录您的API。

生成的OpenAPI文件随后可以被各种程序用来可视化文档。Rocket-okapi目前包含RapiDoc和Swagger UI,但也可以使用其他工具。

支持的OpenAPI规范:3.0.0
支持的Rocket版本(对于rocket_okapi):0.5.0

使用Okapi生成的文档示例:

  • DF Storyteller: RapiDoc, Swagger UI

示例

  • Json web API:简单示例展示Okapi的基础用法。
  • UUID:简单示例展示基础用法,但使用UUID而不是普通的u32/u64 ID。
  • Custom Schema:展示如何向OpenAPI文件添加更多/自定义信息,并将多个模块合并到一个OpenAPI文件中。
  • Secure Request Guard:展示如何在OpenAPI文件中实现认证方法。展示:无认证、API密钥、HTTP认证、OAuth2、OpenID和Cookie。
  • Special types:展示一些更晦涩类型的使用及其用法。(仍在进行中)
  • 还有更多

常见问题

问:我可以从我的OpenAPI文件生成代码吗?

答:不行,这个crate只允许您从代码自动生成OpenAPI文件。有其他crate(尝试)做这件事。所以:

  • (Rust代码(Rocket) --> OpenAPI) == Okapi
  • (OpenAPI --> Rust代码) != Okapi

问:如何记录我的端点?

答:Okapi自动使用大多数地方的Rust文档注释,这包括:

  • 端点函数。
  • 端点函数参数,使用Schemars。除非在其他结构体中使用,否则无法为String和其他默认类型添加文档。
  • 端点函数返回类型,使用Schemars。与参数相同的规则适用。如果是Result<T, E>,可以记录错误代码。

可以使用#[openapi(…)]派生宏提供更多信息。

Schemars也可以用于为实现了#[derive(JsonSchema)]的对象提供更多信息,使用#[schemars(…)]和#[serde(…)]语法。

大多数其他地方也可以增强文档,但可能需要自定义实现。

如果上述方法不够,您始终可以创建自定义的OpenAPI对象。然后可以将其合并到最终的OpenAPI文件中。仅在确实必要时使用此方法!(因为它可能会覆盖其他生成的对象。)

问:我的(diesel)数据库没有实现OpenApiFromRequest。

答:这是因为参数没有出现在路径、查询或正文中。所以这被认为是一个请求守卫。有一个派生宏,但这与#[database("…")]宏不兼容。您可以通过手动实现来解决这个问题,像这样:

use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};
use rocket_okapi::gen::OpenApiGenerator;
use rocket_sync_db_pools::{diesel, database};

#[database("sqlite_logs")]
pub struct MyDB(diesel::SqliteConnection);

impl<'r> OpenApiFromRequest<'r> for MyDB {
    fn from_request_input(
        _gen: &mut OpenApiGenerator,
        _name: String,
        _required: bool,
    ) -> rocket_okapi::Result<RequestHeaderInput> {
        Ok(RequestHeaderInput::None)
    }
}

问:…没有实现JsonSchema?

答:JsonSchema实现由Schemars处理,请确保您为其启用了正确的功能标志。如果仍未实现,请在Schemars仓库中提出问题。

问:我可以向我的OpenAPI规范添加自定义数据吗?

答:可以,请参见Custom Schema示例。如果您想手动合并OpenAPI对象,Okapi也有内置函数。

问:我可以将此与其他Web框架一起使用吗?

答:可以,但目前没有其他实现。但您可以独立使用Okapi crate,并使用Serde创建json或yaml文件。

功能标志

Okapi:

  • impl_json_schema:为Schemars和Okapi类型本身实现JsonSchema。
  • preserve_order:在Schema和OpenAPI文档的所有部分中保持结构体字段的顺序。

Rocket-Okapi:

  • preserve_order:在Schema和OpenAPI文档的所有部分中保持结构体字段的顺序。
  • swagger:启用Swagger UI以呈现文档。
  • rapidoc:启用RapiDoc以呈现文档。
  • uuid:在Rocket和Schemars中启用UUID支持。
  • msgpack:为Rocket启用msgpack支持。(当使用相同的Rocket功能标志时。)
  • secrets:为Rocket启用密钥支持。(当使用相同的Rocket功能标志时。)
  • mtls:为Rocket启用相互TSL。(当使用相同的Rocket功能标志时。)
  • rocket_dyn_templates:启用与rocket_dyn_templates的兼容性。
  • rocket_db_pools:启用与rocket_db_pools的兼容性。
  • rocket_sync_db_pools:启用与rocket_sync_db_pools的兼容性。
  • rocket_ws:启用与rocket_ws的兼容性。

请注意,并非所有Schemars的功能标志都被重新导出或启用。因此,如果您有未实现JsonSchema特征的对象,您可能需要在Schemars中启用功能标志。(确保crate版本匹配)

工作原理

这个crate在Rocket服务器启动时自动生成一个OpenAPI文件。这里简要描述了完成此操作的方式。

Schemars crate为我们提供了所有不同结构和枚举的模式。Okapi不直接实现任何模式,这全部由Schemars处理。

Okapi crate只包含创建OpenAPI文件所需的所有结构。这个crate不包含任何创建它们的代码,只有结构以及将两个OpenAPI结构合并在一起的代码。这个crate可以重用以在其他Web框架中创建OpenAPI支持。

Rocket-Okapi crate包含生成OpenAPI文件并在创建后提供服务的所有代码。此代码通常使用宏执行,例如:mount_endpoints_and_merged_docs!{…}, openapi_get_routes![…], openapi_get_routes_spec![…]和openapi_get_spec![…]。

当Rocket服务器启动(或放置宏的地方)时,OpenAPI文件生成一次。然后这个文件/结构存储在内存中,并在请求时提供。

Rocket-Okapi-codegen crate包含派生宏的代码。#[openapi], rocket_okapi::openapi_spec![…], rocket_okapi::openapi_routes![…]和#[derive(OpenApiFromRequest)]在我们的例子中。由于Rust的限制,这需要在一个单独的crate中。注意:与其他crate相比,derive或codegen crate通常有点难以使用。因此,在更改这里的任何内容之前,建议先获得一些关于派生宏如何工作的经验。

待办事项

  • 测试
  • 文档
  • 基准测试/优化内存使用和分配
    • 自注:看起来很有用
  • 为更多rocket/rocket-contrib类型实现OpenApiFrom___/OpenApiResponder
  • 允许自定义openapi生成设置,例如
    • 自定义json模式生成设置
    • 更改托管文档的路径

许可证

此项目根据MIT许可证授权。

对此项目的所有贡献都将同样获得许可。

完整示例demo

use rocket::get;
use rocket::routes;
use rocket_okapi::openapi_get_routes;
use rocket_okapi::swagger_ui::make_swagger_ui;

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

#[rocket::main]
async fn main() {
    rocket::build()
        .mount("/", openapi_get_routes![index])
        .mount("/swagger", make_swagger_ui(&swagger_ui::Config {
            url: "../openapi.json".to_owned(),
            ..Default::default()
        }))
        .launch()
        .await
        .unwrap();
}

1 回复

Okapi:Rust高性能API开发与扩展工具库

概述

Okapi是一个专为Rust语言设计的高性能API开发框架,提供了一套完整的工具集用于构建、扩展和维护Web API服务。该库结合了Rust的性能优势与现代API开发的最佳实践,支持快速构建类型安全、高性能的RESTful API和GraphQL服务。

核心特性

  • 异步高性能:基于async/await语法,支持高并发处理
  • 类型安全:充分利用Rust的类型系统确保API安全性
  • 中间件支持:灵活的中间件架构
  • OpenAPI集成:自动生成API文档
  • 可扩展设计:模块化架构便于功能扩展

安装方法

在Cargo.toml中添加依赖:

[dependencies]
okapi = "0.4"
okapi-codegen = "0.4"
schemars = "0.8"

基本使用方法

1. 创建基本API端点

use okapi::openapi3::OpenApi;
use rocket_okapi::{openapi, openapi_get_routes, settings::OpenApiSettings};
use rocket::{get, routes};

#[openapi]
#[get("/hello")]
fn hello() -> Json<Value> {
    Json(json!({ "message": "Hello, Okapi!" }))
}

#[launch]
fn rocket() -> _ {
    let settings = OpenApiSettings::default();
    rocket::build()
        .mount("/", openapi_get_routes![settings: hello])
        .mount("/swagger", rocket_okapi::swagger_ui::make_swagger_ui(&swagger_info))
}

2. 定义数据模型和Schema

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

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

#[openapi]
#[get("/users/<id>")]
fn get_user(id: u64) -> Option<Json<User>> {
    // 业务逻辑实现
    Some(Json(User {
        id,
        username: "john_doe".to_string(),
        email: "john@example.com".to_string(),
    }))
}

3. 错误处理示例

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

#[openapi]
#[get("/users/<id>")]
fn get_user(id: u64) -> Result<Json<User>, Status> {
    if id == 0 {
        return Err(Status::NotFound);
    }
    Ok(Json(User {
        id,
        username: "john_doe".to_string(),
        email: "john@example.com".to_string(),
    }))
}

4. 中间件使用示例

use rocket_okapi::request::{OpenApiFromRequest, RequestHeaderInput};

#[rocket::async_trait]
impl<'r> OpenApiFromRequest<'r> for ApiKey {
    type Error = ();

    async fn from_request(request: &'r rocket::Request<'_>) -> Result<Self, Self::Error> {
        // 自定义认证中间件实现
        let key = request.headers().get_one("X-API-Key");
        match key {
            Some(key) if key == "secret" => Ok(ApiKey(key.to_string())),
            _ => Err(()),
        }
    }
}

高级功能

自定义响应Schema

#[openapi]
#[get("/users", format = "json")]
fn list_users() -> Json<Vec<User>> {
    Json(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(),
        },
    ])
}

文件上传支持

#[openapi]
#[post("/upload", data = "<file>")]
async fn upload_file(file: Data<'_>) -> std::io::Result<String> {
    let bytes = file.open(1.megabytes()).into_bytes().await?;
    // 处理上传的文件
    Ok("File uploaded successfully".to_string())
}

完整示例demo

//! Okapi完整API示例
use okapi::openapi3::OpenApi;
use rocket_okapi::{openapi, openapi_get_routes, openapi_post_routes, settings::OpenApiSettings};
use rocket::{get, post, routes, launch};
use rocket::serde::json::{Json, Value, json};
use rocket::serde::{Serialize, Deserialize};
use schemars::JsonSchema;
use rocket::data::Data;
use rocket::http::Status;

// 用户数据结构定义
#[derive(Serialize, Deserialize, JsonSchema, Clone)]
struct User {
    id: u64,
    username: String,
    email: String,
}

// 错误响应结构
#[derive(Serialize, JsonSchema)]
struct ErrorResponse {
    message: String,
    code: u16,
}

// API密钥中间件
struct ApiKey(String);

// 实现OpenApiFromRequest trait用于中间件认证
#[rocket::async_trait]
impl<'r> rocket_okapi::request::OpenApiFromRequest<'r> for ApiKey {
    type Error = ();

    async fn from_request(request: &'r rocket::Request<'_>) -> Result<Self, Self::Error> {
        // 从请求头获取API密钥
        let key = request.headers().get_one("X-API-Key");
        match key {
            Some(key) if key == "secret123" => Ok(ApiKey(key.to_string())),
            _ => Err(()),
        }
    }
}

// 欢迎接口
#[openapi]
#[get("/")]
fn index() -> Json<Value> {
    Json(json!({
        "message": "欢迎使用Okapi API服务",
        "version": "1.0.0"
    }))
}

// 获取用户信息
#[openapi]
#[get("/users/<id>")]
fn get_user(id: u64) -> Result<Json<User>, Status> {
    if id == 0 {
        return Err(Status::NotFound);
    }
    
    // 模拟从数据库获取用户数据
    Ok(Json(User {
        id,
        username: format!("user{}", id),
        email: format!("user{}@example.com", id),
    }))
}

// 获取用户列表
#[openapi]
#[get("/users")]
fn list_users(_key: ApiKey) -> Json<Vec<User>> {
    // 需要有效的API密钥才能访问
    Json(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(),
        },
    ])
}

// 文件上传接口
#[openapi]
#[post("/upload", data = "<file>")]
async fn upload_file(file: Data<'_>) -> std::io::Result<String> {
    // 限制文件大小为1MB
    let bytes = file.open(1.megabytes()).into_bytes().await?;
    
    // 这里可以添加文件处理逻辑
    // 例如保存到磁盘或云存储
    
    Ok(format!("文件上传成功,大小: {} 字节", bytes.len()))
}

// 创建用户接口
#[openapi]
#[post("/users", data = "<user>")]
fn create_user(user: Json<User>) -> Json<Value> {
    // 这里可以添加用户创建逻辑
    // 例如保存到数据库
    
    Json(json!({
        "message": "用户创建成功",
        "user_id": user.id
    }))
}

// Rocket启动函数
#[launch]
fn rocket() -> _ {
    let settings = OpenApiSettings::default();
    
    rocket::build()
        .mount("/", openapi_get_routes![settings: index, get_user, list_users])
        .mount("/", openapi_post_routes![settings: upload_file, create_user])
        .mount("/swagger", rocket_okapi::swagger_ui::make_swagger_ui(
            &rocket_okapi::swagger_ui::SwaggerUIConfig {
                url: "../openapi.json".to_string(),
                ..Default::default()
            }
        ))
}

最佳实践建议

  1. 充分利用Rust的类型系统进行输入验证
  2. 使用Okapi的自动文档生成功能保持API文档最新
  3. 合理使用中间件进行认证和授权处理
  4. 针对高性能场景优化异步处理逻辑
  5. 定期更新Okapi版本以获得最新特性和安全修复

性能优化提示

  • 使用#[inline]标注热点代码路径
  • 合理使用缓存机制减少重复计算
  • 采用连接池管理数据库连接
  • 使用适当的序列化/反序列化策略

Okapi为Rust开发者提供了构建企业级API所需的完整工具链,结合Rust的语言特性,能够创建出安全、高效且易于维护的Web服务。

回到顶部