ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

rust有没有go swag的功能

rust有没有go swag的功能 Rust 生态中与 Go Swag 功能最接近、也最主流的方案是utoipa。它们都是“代码优先”的 OpenAPI 文档生成工具核心思路一致通过注解/宏从源码生成文档而非手动编写 YAML/JSON 文件。 与 Go Swag 的对比虽然功能相似但 Rust 的实现更深度地融入了语言特性核心机制Go Swag 通过// Summary等注释解析utoipa则通过#[utoipa::path]等过程宏在编译期处理与 Rust 类型系统结合更紧密。生态集成utoipa支持 Actix Web、Axum、Rocket 等主流 Rust 框架而 Go Swag 主要面向 Gin、Echo 等。类型驱动配合schemars库能自动推导结构体 Schema大幅减少手写参数/响应模型的繁琐工作。 Rust 主流方案utoipa这是目前社区的事实标准在 GitHub 上活跃度高成熟度也最好。基本用法示例标注结构体用#[derive(ToSchema)]为数据模型生成文档。标注接口用#[utoipa::path(...)]宏描述接口的路径、参数和响应。聚合生成创建一个#[derive(OpenApi)]的结构体引入所有接口路径即可生成完整的 OpenAPI 规范。⚙️ 其他相关工具rocket_okapi/rocket-autodocu如果你使用Rocket框架这两个库提供了更专门的集成方案。oas3-gen这是一个方向相反的工具它从 OpenAPI 文件生成 Rust 代码适合“契约优先”的开发模式。 我能用它做什么自动生成稳定的机器可读 API 文档openapi.json。交互式 UI集成 Swagger UI 或 RapiDoc 等可视化界面方便调试。保持同步代码改文档就改彻底告别“文档和代码不一致”的烦恼。你是用哪个 Web 框架比如 Actix、Axum 或 Rocket告诉我具体框架我可以给你写一个最简的utoipa集成示例。一个完整的utoipaaxum示例包含完整的项目结构和实际可运行的代码。完整项目结构textutoipa-demo/ ├── Cargo.toml └── src/ └── main.rs1.Cargo.tomltoml[package] name utoipa-demo version 0.1.0 edition 2021 [dependencies] axum 0.7 tokio { version 1, features [full] } serde { version 1, features [derive] } utoipa { version 4, features [axum_extras] } utoipa-swagger-ui { version 4, features [axum] } tracing 0.1 tracing-subscriber 0.32.src/main.rs- 完整代码rustuse axum::{ extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Json, Router, }; use serde::{Deserialize, Serialize}; use std::sync::Arc; use utoipa::{OpenApi, ToSchema}; use utoipa_swagger_ui::SwaggerUi; // 数据模型 #[derive(Debug, Serialize, Deserialize, ToSchema, Clone)] pub struct User { /// 用户ID pub id: u64, /// 用户名 pub username: String, /// 邮箱地址 pub email: String, /// 年龄可选 pub age: Optionu32, } #[derive(Debug, Deserialize, ToSchema)] pub struct CreateUserRequest { /// 用户名必填 pub username: String, /// 邮箱地址必填 pub email: String, /// 年龄可选 pub age: Optionu32, } #[derive(Debug, Deserialize, ToSchema)] pub struct UpdateUserRequest { /// 用户名 pub username: OptionString, /// 邮箱 pub email: OptionString, /// 年龄 pub age: Optionu32, } #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct UserListResponse { /// 用户列表 pub users: VecUser, /// 总数量 pub total: u64, } #[derive(Debug, Deserialize, ToSchema)] pub struct QueryParams { /// 分页偏移量 pub offset: Optionu64, /// 分页限制 pub limit: Optionu64, } // 错误响应 #[derive(Debug, Serialize, Deserialize, ToSchema)] pub struct ErrorResponse { pub code: u16, pub message: String, } // 应用状态 type AppState ArcVecUser; // API 处理器 /// 获取所有用户 #[utoipa::path( get, path /api/users, params(QueryParams), responses( (status 200, description 获取用户列表成功, body UserListResponse), (status 500, description 服务器内部错误, body ErrorResponse) ), tag user )] async fn get_users( State(state): StateAppState, Query(params): QueryQueryParams, ) - impl IntoResponse { let offset params.offset.unwrap_or(0) as usize; let limit params.limit.unwrap_or(10) as usize; let users: VecUser state .iter() .skip(offset) .take(limit) .cloned() .collect(); let response UserListResponse { users, total: state.len() as u64, }; (StatusCode::OK, Json(response)) } /// 获取单个用户 #[utoipa::path( get, path /api/users/{id}, params( (id u64, Path, description 用户ID) ), responses( (status 200, description 获取用户成功, body User), (status 404, description 用户不存在, body ErrorResponse), (status 500, description 服务器内部错误, body ErrorResponse) ), tag user )] async fn get_user( State(state): StateAppState, Path(id): Pathu64, ) - impl IntoResponse { match state.iter().find(|user| user.id id) { Some(user) (StatusCode::OK, Json(user.clone())).into_response(), None { let error ErrorResponse { code: 404, message: format!(用户 {} 不存在, id), }; (StatusCode::NOT_FOUND, Json(error)).into_response() } } } /// 创建新用户 #[utoipa::path( post, path /api/users, request_body CreateUserRequest, responses( (status 201, description 创建用户成功, body User), (status 400, description 请求参数错误, body ErrorResponse), (status 500, description 服务器内部错误, body ErrorResponse) ), tag user )] async fn create_user( State(state): StateAppState, Json(payload): JsonCreateUserRequest, ) - impl IntoResponse { // 这里简化处理实际应该写入数据库 let new_id state.len() as u64 1; let user User { id: new_id, username: payload.username, email: payload.email, age: payload.age, }; (StatusCode::CREATED, Json(user)) } /// 更新用户信息 #[utoipa::path( put, path /api/users/{id}, params( (id u64, Path, description 用户ID) ), request_body UpdateUserRequest, responses( (status 200, description 更新用户成功, body User), (status 404, description 用户不存在, body ErrorResponse), (status 500, description 服务器内部错误, body ErrorResponse) ), tag user )] async fn update_user( State(state): StateAppState, Path(id): Pathu64, Json(payload): JsonUpdateUserRequest, ) - impl IntoResponse { match state.iter().find(|user| user.id id) { Some(existing) { // 这里简化处理实际应该更新数据库 let updated User { id: existing.id, username: payload.username.unwrap_or_else(|| existing.username.clone()), email: payload.email.unwrap_or_else(|| existing.email.clone()), age: payload.age.or(existing.age), }; (StatusCode::OK, Json(updated)).into_response() } None { let error ErrorResponse { code: 404, message: format!(用户 {} 不存在, id), }; (StatusCode::NOT_FOUND, Json(error)).into_response() } } } /// 删除用户 #[utoipa::path( delete, path /api/users/{id}, params( (id u64, Path, description 用户ID) ), responses( (status 204, description 删除用户成功), (status 404, description 用户不存在, body ErrorResponse), (status 500, description 服务器内部错误, body ErrorResponse) ), tag user )] async fn delete_user( State(state): StateAppState, Path(id): Pathu64, ) - impl IntoResponse { match state.iter().find(|user| user.id id) { Some(_) StatusCode::NO_CONTENT.into_response(), None { let error ErrorResponse { code: 404, message: format!(用户 {} 不存在, id), }; (StatusCode::NOT_FOUND, Json(error)).into_response() } } } // OpenAPI 文档 #[derive(OpenApi)] #[openapi( paths( get_users, get_user, create_user, update_user, delete_user ), components( schemas( User, CreateUserRequest, UpdateUserRequest, UserListResponse, QueryParams, ErrorResponse ) ), tags( (name user, description 用户管理 API) ), info( title 用户服务 API, description 一个完整的用户管理 REST API 示例, version 1.0.0, contact( name API 支持团队, email supportexample.com, url https://example.com ), license( name MIT, url https://opensource.org/licenses/MIT ) ) )] struct ApiDoc; // 主函数 #[tokio::main] async fn main() { // 初始化日志 tracing_subscriber::fmt() .with_max_level(tracing::Level::DEBUG) .init(); // 准备模拟数据 let users vec![ User { id: 1, username: alice.to_string(), email: aliceexample.com.to_string(), age: Some(25), }, User { id: 2, username: bob.to_string(), email: bobexample.com.to_string(), age: None, }, User { id: 3, username: charlie.to_string(), email: charlieexample.com.to_string(), age: Some(30), }, ]; let state Arc::new(users); // 构建路由 let app Router::new() // API 路由 .route(/api/users, get(get_users).post(create_user)) .route( /api/users/{id}, get(get_user).put(update_user).delete(delete_user), ) // Swagger UI文档界面 .merge(SwaggerUi::new(/swagger-ui).url(/api-docs/openapi.json, ApiDoc::openapi())) .with_state(state); let addr 127.0.0.1:3000; println!( 服务器启动: http://{}, addr); println!( Swagger UI: http://{}/swagger-ui, addr); println!( OpenAPI JSON: http://{}/api-docs/openapi.json, addr); let listener tokio::net::TcpListener::bind(addr).await.unwrap(); axum::serve(listener, app).await.unwrap(); }3. 运行与使用启动服务bashcargo run访问文档Swagger UIhttp://127.0.0.1:3000/swagger-uiOpenAPI JSONhttp://127.0.0.1:3000/api-docs/openapi.jsonAPI 测试bash# 获取所有用户 curl http://127.0.0.1:3000/api/users # 分页查询 curl http://127.0.0.1:3000/api/users?offset0limit2 # 获取单个用户 curl http://127.0.0.1:3000/api/users/1 # 创建用户 curl -X POST http://127.0.0.1:3000/api/users \ -H Content-Type: application/json \ -d {username:dave,email:daveexample.com,age:28}4. 核心特性说明关键宏#[derive(ToSchema)]自动生成结构体的 OpenAPI Schema#[utoipa::path(...)]标注接口描述路径、参数、响应#[derive(OpenApi)]聚合所有接口生成完整文档常见配置项rust// 更复杂的参数描述 #[utoipa::path( get, path /api/search, params( (q String, Query, description 搜索关键词, example rust), (page Optionu32, Query, description 页码, minimum 1, default 1), (sort OptionString, Query, description 排序方式, enum [asc, desc]) ) )] // 多种响应 #[utoipa::path( post, path /api/users, request_body CreateUserRequest, responses( (status 201, description 创建成功, body User), (status 400, description 参数错误, body ErrorResponse), (status 409, description 用户已存在, body ErrorResponse) ), security( (api_key []) ) )]5. 进阶安全认证rust// 在 OpenApi 结构体中添加 security 配置 #[derive(OpenApi)] #[openapi( // ... components( schemas(...), security_schemes( (api_key SecurityScheme::ApiKey(ApiKey::Header(X-API-Key))), (jwt SecurityScheme::Http(Http::Bearer)) ) ) )]6. 与其他框架集成Actix-web版本rust// Cargo.toml utoipa { version 4, features [actix_extras] } utoipa-swagger-ui { version 4, features [actix-web] } // main.rs use utoipa_swagger_ui::SwaggerUi; HttpServer::new(|| { A:new() .service( SwaggerUi::new(/swagger-ui/{_:.*}) .url(/api-docs/openapi.json, ApiDoc::openapi()) ) // ... 其他路由 })这个示例已经包含了完整的 CRUD 操作和自动生成文档功能。你可以直接运行并访问 Swagger UI 进行交互式测试
返回列表