暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

玩转儿 Swagger:分组和多个接口路径的配置

风尘博客 2019-10-11
3261



一、配置多个接口路径

1.1 分析

单个接口路径的配置是:

    **.apis(RequestHandlerSelectors.basePackage("cn.van.swagger.group.web.controller"))

    那我们就来参考一下RequestHandlerSelectors.java
    源码

      public class RequestHandlerSelectors {
      private RequestHandlerSelectors() {
      throw new UnsupportedOperationException();
      }


      public static Predicate<RequestHandler> any() {
      return Predicates.alwaysTrue();
      }


      public static Predicate<RequestHandler> none() {
      return Predicates.alwaysFalse();
      }


      public static Predicate<RequestHandler> withMethodAnnotation(final Class<? extends Annotation> annotation) {
      return new Predicate<RequestHandler>() {
      public boolean apply(RequestHandler input) {
      return input.isAnnotatedWith(annotation);
      }
      };
      }


      public static Predicate<RequestHandler> withClassAnnotation(final Class<? extends Annotation> annotation) {
      return new Predicate<RequestHandler>() {
      public boolean apply(RequestHandler input) {
      return (Boolean)RequestHandlerSelectors.declaringClass(input).transform(RequestHandlerSelectors.annotationPresent(annotation)).or(false);
      }
      };
      }


      private static Function<Class<?>, Boolean> annotationPresent(final Class<? extends Annotation> annotation) {
      return new Function<Class<?>, Boolean>() {
      public Boolean apply(Class<?> input) {
      return input.isAnnotationPresent(annotation);
      }
      };
      }


      private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
      return new Function<Class<?>, Boolean>() {
      public Boolean apply(Class<?> input) {
      return ClassUtils.getPackageName(input).startsWith(basePackage);
      }
      };
      }
      /**
      * Predicate 匹配RequestHandler,并为处理程序方法的类提供基本包名.
      * predicate 包括与所提供的basePackage匹配的所有请求处理程序
      *
      * @param basePackage - base package of the classes
      * @return this
      */
      public static Predicate<RequestHandler> basePackage(final String basePackage) {
      return new Predicate<RequestHandler>() {
      public boolean apply(RequestHandler input) {
      return (Boolean)RequestHandlerSelectors.declaringClass(input).transform(RequestHandlerSelectors.handlerPackage(basePackage)).or(true);
      }
      };
      }


      private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
      return Optional.fromNullable(input.declaringClass());
      }
      }

      我们看到 Swagger
       是通过 Predicate
       的apply()
       方法的返回值来判断是非匹配的,所以我们的方案是:通过改造basePackage()
      方法来实现多包扫描

      1.2 方案测试

      Swagger2Config.java
      配置多包扫描

        @EnableSwagger2
        @Configuration
        public class Swagger2Config {
        /**
        * 定义分隔符
        */
        private static final String SEPARATE = ";";


        @Bean
        public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
        .apiInfo(apiInfo())
        .select()
        // 指定controller存放的目录路径
        单个路径
        .apis(RequestHandlerSelectors.basePackage("cn.van.swagger.group.web.controller"))
        多个路径
        .apis(basePackage("cn.van.swagger.group.web.controller" + SEPARATE +"cn.van.swagger.group.api"))
        .paths(PathSelectors.any())
        .build();
        }


        private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
        .title("这里是Swagger2构建Restful APIs")
        .description("这里是文档描述")
        .termsOfServiceUrl("https://www.dustyblog.cn")
        .version("v1.0")
        .build();
        }
        public static Predicate<RequestHandler> basePackage(final String basePackage) {
        return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
        }


        private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return input -> {
        for (String strPackage : basePackage.split(SEPARATE)) {
        boolean isMatch = input.getPackage().getName().startsWith(strPackage);
        if (isMatch) {
        return true;
        }
        }
        return false;
        };
        }


        private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
        }
        }

        1.3 测试

        api
        包下建立另一个接口类:MultipleController.java

          @RestController
          @RequestMapping("/swagger2")
          @Api(tags = "第二个包的Swagger接口")
          public class MultipleController {
          /**
          * 无参方法
          * @return
          */
          @GetMapping("/sayHello")
          public String sayHello(){
          return "hello MultipleController!";
          }
          }

          启动项目,查看Swagger
          接口

          由上图可知扫描到两个路径的接口,说明配置多个接口路径成功!

          二、接口分组

          2.1 背景

          我们在 Spring Boot
          中定义各个接口是以Controller
          作为第一级维度来进行组织的,Controller
          与具体接口之间的关系是一对多的关系。我们通常将同属一个模块的接口定义在一个Controller
          里。

          默认情况下,Swagger
          是以Controller
          为单位,对接口进行分组管理的,这个分组的元素在Swagger
          中称为Tag
          。但是这里的Tag
          与接口的关系并不是一对多的,它支持更丰富的多对多关系

          2.2 默认分组

          默认情况下,Swagger
          是如何根据Controller
          来组织Tag
          与接口关系的。例如:

          TeacherController.java

            @Api(tags = "教师工作")
            @RestController
            @RequestMapping(value = "/teacher")
            public class TeacherController {


            @PostMapping("/teaching")
            @ApiOperation(value = "教书")
            public String teaching() {
            return "teaching....";
            }


            @PostMapping("/preparing")
            @ApiOperation(value = "备课")
            public String preparing() {
            return "preparing....";
            }
            }

            StudentController.java

              @Api(tags = "学生任务")
              @RestController
              @RequestMapping(value = "/student")
              public class StudentController {


              @ApiOperation("学习")
              @GetMapping("/study")
              public String study() {
              return "study....";
              }
              }

              启动项目,查看此时Swagger
              接口

              到这里,我们还都只是Tag
              Controller
               一一对应

              2.3 合并分组

              Swagger
              中还支持更灵活的分组,查看@Api
              源码,tags
              属性其实是个数组类型

                @Target({ElementType.TYPE})
                @Retention(RetentionPolicy.RUNTIME)
                @Inherited
                public @interface Api {
                String value() default "";


                String[] tags() default {""};
                ...
                }

                由此可以推测:可以通过定义同名的Tag
                来汇总Controller
                中的接口

                上手验证

                定义一个Tag
                为“教学管理”,让这个分组同时包含教师工作和学生任务的所有接口。
                TeacherController.java
                修改为:

                  @Api(tags = {"教师工作", "教学管理"})
                  @RestController
                  @RequestMapping(value = "/teacher")
                  public class TeacherController {


                  @PostMapping("/teaching")
                  @ApiOperation(value = "教书")
                  public String teaching() {
                  return "teaching....";
                  }


                  @PostMapping("/preparing")
                  @ApiOperation(value = "备课")
                  public String preparing() {
                  return "preparing....";
                  }
                  }

                  StudentController.java
                  修改为:

                    @Api(tags = {"学生任务", "教学管理"})
                    @RestController
                    @RequestMapping(value = "/student")
                    public class StudentController {


                    @ApiOperation("学习")
                    @GetMapping("/study")
                    public String study() {
                    return "study....";
                    }
                    }

                    启动项目,Swagger
                    接口如下:

                    此时,学生和教师的接口均在教学管理之下,说明该方案下合并分组成功。

                    2.4 精准分组

                    通过@Api
                    可以实现将指定Controller
                    中的所有接口合并到一个 Tag
                    中,但是如果产品希望:教学管理接口下包含学生任务中所有接口以及教师工作管理中的接口teaching
                    不包含接口preparing
                    )。

                    通过@ApiOperation
                    中的tags
                    属性做更细粒度的接口划分,修改TeacherController.java
                    为:

                      @Api(tags = "教师工作")
                      @RestController
                      @RequestMapping(value = "/teacher")
                      public class TeacherController {


                      @PostMapping("/teaching")
                      @ApiOperation(value = "教书", tags = "教学管理")
                      public String teaching() {
                      return "teaching....";
                      }


                      @PostMapping("/preparing")
                      @ApiOperation(value = "备课")
                      public String preparing() {
                      return "preparing....";
                      }


                      }

                      启动项目,Swagger
                      接口如下:

                      此时,教师工作中的接口preparing
                      不在教学管理中,说明粒度更细的精准分组生效。

                      三、总结及示例代码

                      本文讲解的Swagger
                      的多接口路径扫描和分组在博主开发的进程中,场景不多,但确实用到了,所以稍微整理了下,以防万一。

                      Github 示例代码:https://github.com/vanDusty/SpringBoot-Home/tree/master/springboot-demo-restful/swagger-group


                      文章转载自风尘博客,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                      评论