如何在Rust的Utoipa中简化SwaggerUI代码?抽离重复HTTP错误响应
解决方案
方案1:自定义Rust宏封装重复错误响应
这是最直接的复用方式,通过宏定义把重复的401/403/404响应片段打包,在需要的接口中直接调用即可消除重复代码。
在项目公共模块(比如src/api/swagger.rs)中定义宏:
macro_rules! common_error_responses { () => { (status = 401, description = "Access token is missing or invalid", body = HttpError, example = json!({ "time": "07-05-2024 08:17:33", "status_code": "401", "error_type": "Unauthorized", "reason": "Access token is missing or invalid" }) ), (status = 403, description = "Not allowed to call this endpoint with the current permission", body = HttpError, example = json!({ "time": "07-05-2024 08:17:33", "status_code": "403", "error_type": "AccessForbidden", "reason": "Wrong or missing permissions" }) ), (status = 404, description = "ID not found", body = HttpError, example = json!({ "time": "07-05-2024 08:17:33", "status_code": "404", "error_type": "NotFound", "reason": "ID not found" }) ) }; }
在接口函数中引用该宏:
#[utoipa::path( context_path = "\/user", tag = "user", responses( (status = 200, description = "Endpoint to get all data from a user.", body = Value, example = json!({ "id": 12, "user_id": "TestUser42", "class": "8a", "created_at": "07-04-2004", "user_data": { "traffic": [], "modules": [] } }) ), // 插入公共错误响应 common_error_responses!() ), security( ("bearerAuth" = []) ) )] #[get("\/get")] #[protect(any("STUDENT", "ADMIN", "TEACHER", "MAKERSPACE"), error = "access_denied")] pub async fn get_user( claims: Option<web::ReqData<Claims>> ) -> impl Responder { // ... 原有实现逻辑 }
方案2:用Utoipa的Response结构体定义常量
如果需要更结构化的管理,可以预先定义Response实例作为常量,在path宏的responses中直接引用。
在公共模块定义常量:
use utoipa::openapi::Response; use utoipa::ToSchema; use serde_json::json; #[derive(ToSchema)] pub struct HttpError; pub const RESPONSE_401: Response = Response::new( "Access token is missing or invalid", Some(HttpError::schema_ref()), Some(json!({ "time": "07-05-2024 08:17:33", "status_code": "401", "error_type": "Unauthorized", "reason": "Access token is missing or invalid" })), ); pub const RESPONSE_403: Response = Response::new( "Not allowed to call this endpoint with the current permission", Some(HttpError::schema_ref()), Some(json!({ "time": "07-05-2024 08:17:33", "status_code": "403", "error_type": "AccessForbidden", "reason": "Wrong or missing permissions" })), ); pub const RESPONSE_404: Response = Response::new( "ID not found", Some(HttpError::schema_ref()), Some(json!({ "time": "07-05-2024 08:17:33", "status_code": "404", "error_type": "NotFound", "reason": "ID not found" })), );
在接口中引用常量:
use crate::api::swagger::{RESPONSE_401, RESPONSE_403, RESPONSE_404}; #[utoipa::path( context_path = "\/user", tag = "user", responses( (status = 200, description = "Endpoint to get all data from a user.", body = Value, example = json!({ "id": 12, "user_id": "TestUser42", "class": "8a", "created_at": "07-04-2004", "user_data": { "traffic": [], "modules": [] } }) ), (status = 401, response = RESPONSE_401), (status = 403, response = RESPONSE_403), (status = 404, response = RESPONSE_404) ), security( ("bearerAuth" = []) ) )] // ... 后续代码不变
关于JSON文件存储的说明
如果想把错误示例单独存到JSON文件,可通过include_str!读取文件内容并解析,但需确保编译期路径正确,且JSON格式无错误(否则编译会失败)。示例如下:
创建swagger/error_examples/401.json文件:
{ "time": "07-05-2024 08:17:33", "status_code": "401", "error_type": "Unauthorized", "reason": "Access token is missing or invalid" }
在宏或常量中引用:
example = serde_json::from_str(include_str!("../../../swagger/error_examples/401.json")).unwrap()
这种方式灵活性不如前两种,适合需要统一管理大量外部示例的场景。
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

