JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容

时间:2022-09-23 00:35:59

大家好,又见面啦。

在前一篇文档《JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率》中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让Swagger支持自动从指定的枚举类生成接口文档中的字段描述的实现思路。

其实swagger作为一个被广泛使用的在线接口文档辅助工具,上手会用很容易,但想用好却还是需要一定功夫的。所以呢,本篇文档就和大家一起来聊一聊如何用好swagger,让其真正的成为我们项目交付过程中的神兵利器

更改接口文档总标题与描述

默认的情况下,Swagger的界面整个文档的名称以及描述内容都是通用值,这会让人拿到文档之后比较困惑,无法知晓这是哪个项目哪个系统哪个服务提供的接口,也不知道接口是哪个团队负责,哪位开发人员维护的。

比如下面这样:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

为了体现出接口文档的专业性,让人更容易知晓此接口文档所属系统对应版本维护团队等信息,我们可以在代码中根据需要自定义相关的内容。

比如:

@Bean
public Docket createRestApi() {
    ApiInfo apiInfo = new ApiInfoBuilder()
        .title(\"资源管理系统接口文档\")
        .description(\"资源管理模块对外供APP/WEB端调用的接口详细文档描述\")
        .version(\"v1.0.0\")
        .contact(new Contact(\"架构悟道\", \"https://juejin.cn/user/1028798616709294\",\"veezean@outlook.com\"))
        .termsOfServiceUrl(\"https://juejin.cn/user/1028798616709294\")
        .license(\"Apache License\")
        .licenseUrl(\"http://xxx\")
        .build();
    return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().build();
}

重新启动并查看界面,可以发现界面上相关内容已经变更为我们自定义的内容了,是不是比改动前显得更加明晰与专业了?

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

上述swagger中支持自定义的描述性的字段信息,梳理如下:

字段 含义描述
title 接口文档的文档标题
description 接口文档的详细整体描述说明
version 接口文档对应的版本信息
termsOfServiceUrl 此接口文档的提供团队对应的团队url地址
contact 负责此部分接口的联系人信息,包含姓名邮箱主页url地址等
license 指定接口所遵循的License协议版本
licenseUrl 此接口所遵循的License协议对应的详细介绍url地址

按需显示/隐藏相关接口内容

手动编写接口文档的时候,我们可以根据实际情况灵活的去控制需要写入到文档中的接口内容、以及接口的请求响应体中的字段信息 —— 因为并不是系统中提供的所有的接口都需要体现在接口文档中暴露给调用方去知晓的,比如有一些系统状态监控类的接口,只需要内部使用即可。

对于Swagger而言,生成接口文档的时候,默认是扫描所有的@Controller中的全部接口方法全部显示到文档中,但其也贴心地考虑到了实际应用中的这种按需隐藏或者展示接口内容的诉求,并提供了多种不同的方式来支持。

下面一起来看下。

针对单个接口进行隐藏

在单个接口方法的上方添加 @ApiOperation 注解说明,并指定 hidden = true即可将该接口从swagger界面能上隐藏:

@GetMapping(\"/test\")
@ApiOperation(value = \"内部测试接口\", hidden = true)
public String test() {
    return \"OK\";
}

启动进程,查看Swagger界面,发现该接口没有出现在界面上:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

隐藏整个Controller类中的接口

如果整个Controller类下面所有的接口都需要隐藏,则可以在Conntroller类上添加上@ApiIgnore注解可以了。

@RestController
@RequestMapping(\"/test\")
@ApiIgnore
public class TestController {
// ... ignore ...
}

改动后重启进程,再打开swagger界面,发现TestController整个类的接口都没有显示。

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

这里补充一句,因为用于描述Controller类的接口含义的注解@Api中也有个hidden属性,而且看其源码注释说明,如果设置hidden=true,应该也是将该Controller类整体隐藏。但是实际上测试发现并没有生效,这个实际使用的时候要小心这一点(基于swagger 2.7.0版本试验,不确定是否为BUG)。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Api {
    /**
     * Hides the operations under this resource.
     *
     * @return true if the api should be hidden from the swagger documentation
     */
    boolean hidden() default false;
}

仅显示指定package路径下的接口

我们的项目里面经常会依赖或者引用一些三方jar包,而这些三方jar中有的时候也会提供一些接口,也会出现在我们的接口文档中,这样就会显得接口文档中存在很多不确定的内容

比如:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

因为这部分逻辑并非业务代码中提供的,所以我们没法按照上面的方式,修改源码添加hidden=true的方式来控制其不显示。这个时候,就需要按照package进行白名单控制的能力了。swagger还支持根据给定的basePackage以及paths进行组合控制,仅显示给定包下指定路径下的接口。


@Bean
public Docket createRestApi() {
    ApiInfo apiInfo = new ApiInfoBuilder()
            .title(\"资源管理系统接口文档\")
            .description(\"资源管理模块对外供APP/WEB端调用的接口详细文档描述\")
            .version(\"v1.0.0\")
            .build();
    ApiSelectorBuilder selectorBuilder = (new Docket(DocumentationType.SWAGGER_2)).apiInfo(apiInfo).select();
    selectorBuilder.apis(RequestHandlerSelectors.basePackage(\"com.jiagouwudao.resmanage\"));
    selectorBuilder.paths(PathSelectors.any());
    return selectorBuilder.build();
}

这样就可以保证出现在接口文档里面的都是我们自己定义的接口了。重新启动并刷新界面,会发现,只有指定package目录下的Controller接口显示在swagger界面上了。

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

隐藏响应中不愿暴露的属性

在项目开发过程中,如果我们的代码没有做强制的VODO隔断,出于减少编码量考虑,可能会使用同一个对象进行内部处理以及外部交互。比如:

定义一个OperateLog对象,为数据库中T_OPERATE_LOG表所对应的实体类,用于记录每个用户的操作行为;同时也作为recordOperateLog接口的请求Body体,用于传递需要记录的用户操作信息。

在上面的例子中:

  • 作为数据表实体类进行逻辑处理的时候,需要用到唯一主键id信息
  • 作为recordOperateLog接口的请求Body体时,调用方是不需要指定这条记录的ID值的(ID值会在存储到DB的时候自动由DB生成唯一自增主键)

这种场景下,我们就希望提供出去的接口文档中,在对recordOperateLog接口的请求body体中字段说明的时候,就不要体现出id字段,避免让调用方产生疑惑,不知道id调用的时候应该如何赋值。我们可以通过在指定字段上添加@ApiModelProperty注解并指定hidden = true来将其从接口文档中隐藏掉。

比如:

@Data
@ApiModel(\"操作记录信息\")
public class OperateLog {
    @ApiModelProperty(value = \"记录唯一ID,无实际意义\", hidden = true)
    private long id;
    @ApiModelProperty(\"操作类型,取值说明: 1,新增;2,更新;3,删除;4,查询\")
    private int operateType;
    @ApiModelProperty(\"操作用户\")
    private String user;
    @ApiModelProperty(value = \"操作详情\")
    private String detail;
}

则界面中的接口文档不会显示id的有关信息(注意:仅接口文档中不体现,不会影响具体请求或者响应中此字段的实际值)。

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

关闭生产环境的swagger

考虑到生产环境的安全性,对于一些比较重要的系统,我们一般不太愿意将生产环境的接口文档暴露出来,避免对系统的运行埋下隐患。

SpringBoot项目中,我们会为不同的环境提供不同的配置文件, 然后在启动的时候使用 --spring.active.profile 来指定加载哪一份配置。

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

如果需要使Swagger可以被访问,我们可以通过代码中添加@EnableSwagger2注解的方式来实现。若限制仅在开发或测试环境上允许swagger访问而生产环境不允许打开,则只需要让这个添加了@EnableSwagger2注解的类根据当前的运行环境来决定是否加载就可以了。借助SpringBoot提供的@Profile注解,我们可以这样来实现:

@Configuration
@EnableSwagger2
@Profile({\"DEV\", \"TEST\"})
public class SwaggerConfig {

}

这样,就可以让SwaggerConfig类在profile=PROD的时候不会被加载,也就不会开启swagger的开关。使用 --spring.active.profile=PROD启动进程,尝试访问swagger界面,会发现无法打开:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

给Swagger换个皮肤

默认的swagger界面所有内容都罗列居中显示,然后需要一层层的展开去,操作上面不太方便,整体界面风格也不太符合一个“文档”的样子。为了提升使用体验,可以借助开源的knife4j框架来让swagger变得更加好用。

使用方式很简单,在已有的swagger依赖的基础上,在pom.xml中新增如下引用依赖:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-ui</artifactId>
    <version>2.0.4</version>
</dependency>

启动进程后,访问 doc.html 页面,比如 http://127.0.0.1:8088/doc.html,可以发现一个更加符合接口文档体验的新的界面:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

当然,这里我们使用了knife4j最简单的一个“换肤”的特性,而作为一款优秀的开源工具,knife4j所提供的能力远不止这些,有兴趣的可以点击此处详细了解一下。

总结

好啦,关于如何补全Swagger接口的描述内容、如何自主决定某些内容的显示与隐藏等相关的内容,这里就给大家分享到这里啦。关于本篇内容你有什么自己的想法或独到见解么?欢迎在评论区一起交流探讨下吧。

????????另外:

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"

我是悟道,聊技术、又不仅仅聊技术~

如果觉得有用,请点赞 + 关注让我感受到您的支持。也可以关注下我的公众号【架构悟道】,获取更及时的更新。

期待与你一起探讨,一起成长为更好的自己。

\"JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容\"