Quarkus中如何将MicroProfile/OpenAPI注解移至接口生成Swagger文件并保持代码整洁
问题
在Quarkus中,我需要定义API以生成可提供给消费者的Swagger-UI JSON/YAML文件。目前在REST端点中定义大量注解可实现该需求,示例代码如下:
@POST @Operation(summary = "creates a new product") @APIResponse(responseCode = "201", description = "product creation successful") @APIResponse(responseCode = "500", description = "Server unavailable") @Path("/") @RequestBody(content = @Content(examples = { @ExampleObject( name = CreateProductRequest.EXAMPLE1_NAME, description = CreateProductRequest.EXAMPLE1_DESCRIPTION, value = CreateProductRequest.EXAMPLE1_VALUE ) })) @APIResponse(content = @Content(examples = { @ExampleObject( name = CreateProductResponse.EXAMPLE1_NAME, description = CreateProductResponse.EXAMPLE1_DESCRIPTION, value = CreateProductResponse.EXAMPLE1_VALUE ) })) @ResponseStatus(201) public CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException { return productManager.create(new RequestContext(1, 1), createProductRequest); }
但这种方式会让REST端点代码几乎难以阅读(这已是较简短的示例)。我期望通过定义接口并让REST端点实现的方式解决该问题,但尝试的方案并未生效——框架完全忽略接口中的注解,生成的swagger.json/yaml文件中不会包含这些注解信息,我的尝试代码如下:
@Path("/api/v1/products") public class ProductControllerV1 implements ProductApiV1 { @POST @Path("/") @Override public CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException { return productManager.create(new RequestContext(1, 1), createProductRequest); } }
@Tag( name = "Product Controller V1", description = "Product Operations, for testing many different use-cases" ) @Path("/api/v1/products") public interface ProductApiV1 { @POST @Operation(summary = "creates a new product") @APIResponse(responseCode = "201", description = "product creation successful") @APIResponse(responseCode = "500", description = "Server unavailable") @Path("/") @RequestBody(content = @Content(examples = { @ExampleObject( name = CreateProductRequest.EXAMPLE1_NAME, description = CreateProductRequest.EXAMPLE1_DESCRIPTION, value = CreateProductRequest.EXAMPLE1_VALUE ) })) @APIResponse(content = @Content(examples = { @ExampleObject( name = CreateProductResponse.EXAMPLE1_NAME, description = CreateProductResponse.EXAMPLE1_DESCRIPTION, value = CreateProductResponse.EXAMPLE1_VALUE ) })) @ResponseStatus(201) CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException; }
现寻求帮助:
- 确认通过接口定义注解的方案是否可行,并提供Quarkus中此类接口的定义示例;
- 提供更优实现方案,在保持REST端点简洁易维护的同时,仍能在源码附近维护Swagger-UI的API定义。
回答
1. 接口定义注解方案的可行性与示例
该方案完全可行,问题出在Quarkus默认不会自动继承接口上的OpenAPI注解。只需给接口添加@InheritedOpenAPIDefinition注解,即可让框架识别接口中的注解并继承到实现类。
修正后的接口示例:
@InheritedOpenAPIDefinition @Tag( name = "Product Controller V1", description = "Product Operations, for testing many different use-cases" ) @Path("/api/v1/products") public interface ProductApiV1 { @POST @Operation(summary = "creates a new product") @APIResponse(responseCode = "201", description = "product creation successful") @APIResponse(responseCode = "500", description = "Server unavailable") @Path("/") @RequestBody(content = @Content(examples = { @ExampleObject( name = CreateProductRequest.EXAMPLE1_NAME, description = CreateProductRequest.EXAMPLE1_DESCRIPTION, value = CreateProductRequest.EXAMPLE1_VALUE ) })) @APIResponse(content = @Content(examples = { @ExampleObject( name = CreateProductResponse.EXAMPLE1_NAME, description = CreateProductResponse.EXAMPLE1_DESCRIPTION, value = CreateProductResponse.EXAMPLE1_VALUE ) })) @ResponseStatus(201) CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException; }
实现类可简化为:
@Path("/api/v1/products") public class ProductControllerV1 implements ProductApiV1 { @Override public CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException { return productManager.create(new RequestContext(1, 1), createProductRequest); } }
@InheritedOpenAPIDefinition是核心,它会告知Quarkus的OpenAPI扩展将接口上的注解同步到实现类中。
2. 更优实现方案
除接口继承外,还有两种更简洁的方案:
方案一:自定义注解聚合
将重复的OpenAPI注解封装到自定义注解中,减少端点代码的注解冗余:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Operation(summary = "creates a new product") @APIResponse(responseCode = "201", description = "product creation successful") @APIResponse(responseCode = "500", description = "Server unavailable") @RequestBody(content = @Content(examples = { @ExampleObject( name = CreateProductRequest.EXAMPLE1_NAME, description = CreateProductRequest.EXAMPLE1_DESCRIPTION, value = CreateProductRequest.EXAMPLE1_VALUE ) })) @APIResponse(content = @Content(examples = { @ExampleObject( name = CreateProductResponse.EXAMPLE1_NAME, description = CreateProductResponse.EXAMPLE1_DESCRIPTION, value = CreateProductResponse.EXAMPLE1_VALUE ) })) @ResponseStatus(201) public @interface CreateProductApi { }
端点方法只需使用这个自定义注解:
@POST @Path("/") @CreateProductApi public CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException { return productManager.create(new RequestContext(1, 1), createProductRequest); }
方案二:独立OpenAPI配置文件
将Swagger定义单独写在src/main/resources/META-INF/openapi.yaml(或.json)文件中,与代码分离但保持在源码目录内。此时端点代码仅保留基础REST注解:
@POST @Path("/") @ResponseStatus(201) public CreateProductResponse create(CreateProductRequest createProductRequest) throws ValidationException { return productManager.create(new RequestContext(1, 1), createProductRequest); }
对应的openapi.yaml示例:
openapi: 3.0.3 info: title: Product API version: 1.0.0 paths: /api/v1/products/: post: summary: creates a new product requestBody: content: application/json: examples: example1: name: ${CreateProductRequest.EXAMPLE1_NAME} description: ${CreateProductRequest.EXAMPLE1_DESCRIPTION} value: ${CreateProductRequest.EXAMPLE1_VALUE} responses: '201': description: product creation successful content: application/json: examples: example1: name: ${CreateProductResponse.EXAMPLE1_NAME} description: ${CreateProductResponse.EXAMPLE1_DESCRIPTION} value: ${CreateProductResponse.EXAMPLE1_VALUE} '500': description: Server unavailable tags: - name: Product Controller V1 description: Product Operations, for testing many different use-cases
Quarkus会自动加载该文件,并与代码中的注解合并生成最终的Swagger-UI文件,还支持通过${}引用代码中的常量。
内容的提问来源于stack exchange,提问作者funkrusher
相关产品推荐
相关产品推荐

