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

如何在springdoc-openapi中用Schema引用作为接口的oneOf/anyOf响应?

SpringDoc-OpenAPI 相关问题解决方案

问题1:通过Schema引用配置oneOf/anyOf响应

由于@ApiResponse的@Schema注解中oneOf字段仅支持传入类对象,无法直接引用已定义的Schema,需要通过自定义OpenApiCustomiser修改OpenAPI模型实现:

  1. 先在配置类中定义基础Schema和衍生的ErrorOne、ErrorTwo引用:
@Configuration
public class OpenApiConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        // 定义基础ErrorObject Schema
        Schema<ErrorObject> errorObjectSchema = new Schema<>()
                .type("object")
                .addProperty("code", new IntegerSchema())
                .addProperty("message", new StringSchema());

        // 基于ErrorObject创建两个无独立类的Schema引用
        Schema<?> errorOneSchema = new Schema<>().$ref("#/components/schemas/ErrorObject").description("错误类型1");
        Schema<?> errorTwoSchema = new Schema<>().$ref("#/components/schemas/ErrorObject").description("错误类型2");

        return new OpenAPI()
                .components(new Components()
                        .addSchemas("ErrorObject", errorObjectSchema)
                        .addSchemas("ErrorOne", errorOneSchema)
                        .addSchemas("ErrorTwo", errorTwoSchema));
    }
}
  1. 创建OpenApiCustomiser定位目标接口,修改响应的Schema为oneOf引用:
@Bean
public OpenApiCustomiser oneOfResponseCustomiser() {
    return openApi -> {
        // 遍历所有接口,通过operationId精准定位目标操作
        openApi.getPaths().values().forEach(pathItem -> {
            pathItem.readOperations().forEach(operation -> {
                if ("getUserInfo".equals(operation.getOperationId())) {
                    // 获取目标响应(示例为400状态码)
                    ApiResponse targetResponse = operation.getResponses().get("400");
                    if (targetResponse != null) {
                        // 构建包含两个Schema引用的oneOf Schema
                        Schema<?> oneOfSchema = new Schema<>()
                                .oneOf(List.of(
                                        new Schema<>().$ref("#/components/schemas/ErrorOne"),
                                        new Schema<>().$ref("#/components/schemas/ErrorTwo")
                                ));
                        // 更新响应的Content配置
                        targetResponse.setContent(new Content()
                                .addMediaType(MediaType.APPLICATION_JSON_VALUE,
                                        new MediaType().schema(oneOfSchema)));
                    }
                }
            });
        });
    };
}

问题2:添加全局oneOf/anyOf响应(与接口自身响应共存)

可以通过GlobalOpenApiCustomiser实现全局响应配置,支持与接口自身定义的响应共存:

场景1:为所有接口添加独立状态码的全局oneOf错误响应

给所有接口统一添加400状态码的oneOf错误响应,与接口自身的200/201等响应共存:

@Bean
public GlobalOpenApiCustomiser globalOneOfErrorCustomiser() {
    return openApi -> {
        // 构建全局oneOf错误Schema
        Schema<?> globalOneOfSchema = new Schema<>()
                .oneOf(List.of(
                        new Schema<>().$ref("#/components/schemas/ErrorOne"),
                        new Schema<>().$ref("#/components/schemas/ErrorTwo")
                ));

        MediaType jsonMediaType = new MediaType().schema(globalOneOfSchema);
        ApiResponse globalErrorResponse = new ApiResponse()
                .description("全局错误响应:包含两种错误类型")
                .content(new Content().addMediaType(MediaType.APPLICATION_JSON_VALUE, jsonMediaType));

        // 给所有接口操作添加400状态码的全局响应
        openApi.getPaths().values().forEach(pathItem -> {
            pathItem.readOperations().forEach(operation -> {
                // 若接口已定义400响应,此处会直接替换;如需合并可先判断后处理
                operation.getResponses().put("400", globalErrorResponse);
            });
        });
    };
}

场景2:同一状态码下合并接口自身响应与全局oneOf响应

在同一个状态码(比如200)下,同时支持接口自身的业务响应和全局错误响应,通过anyOf合并两者:

@Bean
public GlobalOpenApiCustomiser mergeResponseCustomiser() {
    return openApi -> {
        Schema<?> globalOneOfSchema = new Schema<>()
                .oneOf(List.of(
                        new Schema<>().$ref("#/components/schemas/ErrorOne"),
                        new Schema<>().$ref("#/components/schemas/ErrorTwo")
                ));

        openApi.getPaths().values().forEach(pathItem -> {
            pathItem.readOperations().forEach(operation -> {
                ApiResponse existing200 = operation.getResponses().get("200");
                if (existing200 != null) {
                    MediaType existingMediaType = existing200.getContent().get(MediaType.APPLICATION_JSON_VALUE);
                    if (existingMediaType != null) {
                        // 合并原有Schema与全局oneOf Schema为anyOf
                        Schema<?> mergedSchema = new Schema<>()
                                .anyOf(List.of(existingMediaType.getSchema(), globalOneOfSchema));
                        existingMediaType.setSchema(mergedSchema);
                    }
                }
            });
        });
    };
}

内容的提问来源于stack exchange,提问作者Hooman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 04:36:18