You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 21:20:11