美文网首页
Swagger自定义文档插件

Swagger自定义文档插件

作者: 清蒸三文鱼_ | 来源:发表于2021-05-13 10:57 被阅读0次

    背景

    在原有文档的基础上, 丰富或更改文档的信息 , 例如在@ApiOperation中自动添加权限码的说明, 那么可以通过Swagger的OperationBuilderPlugin插件来实现

    Maven

     <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <version>2.3.6.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger-ui</artifactId>
                <version>2.9.2</version>
            </dependency>
            <dependency>
                <groupId>io.springfox</groupId>
                <artifactId>springfox-swagger2</artifactId>
                <version>2.9.2</version>
            </dependency>
        </dependencies>
    

    Code

    测试入口和主程序

    @SpringBootApplication
    @RestController
    @EnableSwagger2
    public class AppDemo {
        public static void main(String[] args) {
            SpringApplication.run(AppDemo.class, args);
        }
        @GetMapping("swaggerTest1")
        @ApiOperation(value = "swagger测试", notes = "测试1")
        @AutoPermission
        public LocalDateTime swaggerTest1() {
            return LocalDateTime.now();
        }
    }
    

    自定义注解类

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AutoPermission {
        String value() default "";
        boolean auto() default true;
    }
    

    Swagger插件类

    自定义的插件Bean加载, 优先级要迟于Swagger自带的插件, 否则文档中的值会被覆盖, 如被自带的notes解析类覆盖OperationNotesReader

    import com.google.common.base.Optional;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.OperationBuilderPlugin;
    import springfox.documentation.spi.service.contexts.OperationContext;
    import springfox.documentation.spring.web.DescriptionResolver;
    import springfox.documentation.swagger.common.SwaggerPluginSupport;
    
    @Component
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
    public class CustomOperationPlugin implements OperationBuilderPlugin {
        private final DescriptionResolver descriptions;
        @Autowired
        public CustomOperationPlugin(DescriptionResolver descriptions) {
            this.descriptions = descriptions;
        }
        @Override
        public void apply(OperationContext context) {
            Optional<ApiOperation> apiOperationOp = context.findAnnotation(ApiOperation.class);
            if (apiOperationOp.isPresent()) {
                String notes = apiOperationOp.get().notes();
                Optional<AutoPermission> autoPermissionOp = context.findAnnotation(AutoPermission.class);
                //添加权限码到notes中
                if (autoPermissionOp.isPresent()) {
                    AutoPermission autoPermission = autoPermissionOp.get();
                    String code = autoPermission.auto() ? context.requestMappingPattern().toUpperCase() : autoPermission.value();
                    notes = notes + " 自定义生成的权限码:" + code;
                }
                //编辑api文档信息, notes
                context.operationBuilder().notes(descriptions.resolve(notes));
            }
        }
        @Override
        public boolean supports(DocumentationType documentationType) {
            return SwaggerPluginSupport.pluginDoesApply(documentationType);
        }
    }
    
    OperationNotesReader

    获取handleMethod

    如果需要更复杂的操作, 如根据方法和类上的信息更改Swagger文档, 不是必须的视情况而定

        private Method getHandleMethod(OperationContext context, boolean useReflect) {
            //通过反射方式获取Method
            if (useReflect){
                Field requestContextField = ReflectionUtils.findField(OperationContext.class, "requestContext");
                requestContextField.setAccessible(true);
                RequestMappingContext requestMappingContext = (RequestMappingContext) ReflectionUtils.getField(requestContextField, context);
    
                Field handlerField = ReflectionUtils.findField(RequestMappingContext.class, "handler");
                handlerField.setAccessible(true);
                RequestHandler requestHandler = (RequestHandler) ReflectionUtils.getField(handlerField, requestMappingContext);
                return requestHandler.getHandlerMethod().getMethod(); 
            }
            
            //通过url查找Method
            return context.getDocumentationContext().getRequestHandlers().stream()
                    .filter(v -> v.getPatternsCondition().getPatterns().contains(context.requestMappingPattern()))
                    .findFirst().map(v -> v.getHandlerMethod().getMethod()).get();
        }
    

    验证

    输入文档地址 http://localhost:8080/swagger-ui.html , 由图可知权限码已自动添加到文档的描述中

    其他方式的实现

    实现的方案有多种(任意一种均可), 可以覆盖原有的文档相关的类, 可以加入web的拦截器; 主要是分享一下思路, 具体的流程可以去debug查看SpringFox源码

    1. DocumentationCache扩展

    页面的请求 (Swagger2ControllerWebMvc) 最终会落到DocumentationCache, 所以在文档缓存添加的时候, 用反射更改文档字段的信息


    示例代码如下,也可以通过Spring的Bean工厂对DocumentationCache先销毁再注册:
    @Component
    @Primary
    public class DocumentationCacheExt extends DocumentationCache {
        @Override
        public void addDocumentation(Documentation documentation) {
            ApiListing apiListing = new ArrayList<>(documentation.getApiListings().get("app-demo")).get(0);
            Operation operation = apiListing.getApis().get(0).getOperations().get(0);
            Field notesField = ReflectionUtils.findField(Operation.class, "notes");
            notesField.setAccessible(true);
            String notes = (String) ReflectionUtils.getField(notesField, operation);
            ReflectionUtils.setField(notesField, operation, notes + " 我的缓存测试ABC");
            super.addDocumentation(documentation);
        }
    }
    

    2. OperationNotesReader扩展

    @Component
    @Primary
    public class OperationNotesReaderExt extends OperationNotesReader {
        private final DescriptionResolver descriptions;
        @Autowired
        public OperationNotesReaderExt(DescriptionResolver descriptions) {
            super(descriptions);
            this.descriptions = descriptions;
        }
        @Override
        public void apply(OperationContext context) {
            Optional<ApiOperation> methodAnnotation = context.findAnnotation(ApiOperation.class);
            if (methodAnnotation.isPresent() && StringUtils.hasText(methodAnnotation.get().notes())) {
                //do something
                context.operationBuilder().notes(descriptions.resolve(methodAnnotation.get().notes()));
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Swagger自定义文档插件

          本文链接:https://www.haomeiwen.com/subject/rpmgdltx.html