如何在springdoc-openapi中用Schema引用作为接口的oneOf/anyOf响应?
SpringDoc-OpenAPI 相关问题解决方案
问题1:通过Schema引用配置oneOf/anyOf响应
由于@ApiResponse的@Schema注解中oneOf字段仅支持传入类对象,无法直接引用已定义的Schema,需要通过自定义OpenApiCustomiser修改OpenAPI模型实现:
- 先在配置类中定义基础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)); } }
- 创建
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
相关产品推荐
相关产品推荐

