大家好,又见面啦。
在前一篇文档《JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率》中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让Swagger支持自动从指定的枚举类生成接口文档中的字段描述的实现思路。
其实swagger作为一个被广泛使用的在线接口文档辅助工具,上手会用很容易,但想用好却还是需要一定功夫的。所以呢,本篇文档就和大家一起来聊一聊如何用好swagger,让其真正的成为我们项目交付过程中的神兵利器。
默认的情况下,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();
}
重新启动并查看界面,可以发现界面上相关内容已经变更为我们自定义的内容了,是不是比改动前显得更加明晰与专业了?

上述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界面,发现该接口没有出现在界面上:

如果整个Controller类下面所有的接口都需要隐藏,则可以在Conntroller类上添加上@ApiIgnore注解可以了。
@RestController
@RequestMapping("/test")
@ApiIgnore
public class TestController {
// ... ignore ...
}
改动后重启进程,再打开swagger界面,发现TestController整个类的接口都没有显示。

这里补充一句,因为用于描述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;
}
我们的项目里面经常会依赖或者引用一些三方jar包,而这些三方jar中有的时候也会提供一些接口,也会出现在我们的接口文档中,这样就会显得接口文档中存在很多不确定的内容。
比如:

因为这部分逻辑并非业务代码中提供的,所以我们没法按照上面的方式,修改源码添加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界面上了。

在项目开发过程中,如果我们的代码没有做强制的VO、DO隔断,出于减少编码量考虑,可能会使用同一个对象进行内部处理以及外部交互。比如:
定义一个OperateLog对象,为数据库中T_OPERATE_LOG表所对应的实体类,用于记录每个用户的操作行为;同时也作为recordOperateLog接口的请求Body体,用于传递需要记录的用户操作信息。
在上面的例子中:
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的有关信息(注意:仅接口文档中不体现,不会影响具体请求或者响应中此字段的实际值)。

考虑到生产环境的安全性,对于一些比较重要的系统,我们一般不太愿意将生产环境的接口文档暴露出来,避免对系统的运行埋下隐患。
在SpringBoot项目中,我们会为不同的环境提供不同的配置文件, 然后在启动的时候使用 --spring.active.profile 来指定加载哪一份配置。

如果需要使Swagger可以被访问,我们可以通过代码中添加@EnableSwagger2注解的方式来实现。若限制仅在开发或测试环境上允许swagger访问而生产环境不允许打开,则只需要让这个添加了@EnableSwagger2注解的类根据当前的运行环境来决定是否加载就可以了。借助SpringBoot提供的@Profile注解,我们可以这样来实现:
@Configuration
@EnableSwagger2
@Profile({"DEV", "TEST"})
public class SwaggerConfig {
}
这样,就可以让SwaggerConfig类在profile=PROD的时候不会被加载,也就不会开启swagger的开关。使用 --spring.active.profile=PROD启动进程,尝试访问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,可以发现一个更加符合接口文档体验的新的界面:


当然,这里我们使用了knife4j最简单的一个“换肤”的特性,而作为一款优秀的开源工具,knife4j所提供的能力远不止这些,有兴趣的可以点击此处详细了解一下。
好啦,关于如何补全Swagger接口的描述内容、如何自主决定某些内容的显示与隐藏等相关的内容,这里就给大家分享到这里啦。关于本篇内容你有什么自己的想法或独到见解么?欢迎在评论区一起交流探讨下吧。
??另外:

我是悟道,聊技术、又不仅仅聊技术~
如果觉得有用,请点赞 + 关注让我感受到您的支持。也可以关注下我的公众号【架构悟道】,获取更及时的更新。
期待与你一起探讨,一起成长为更好的自己。

我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.
Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur
在Java中,可以像这样从一个字符串创建一个IO流:Readerr=newStringReader("mytext");我希望能够在Ruby中做同样的事情,这样我就可以获取一个字符串并将其视为一个IO流。 最佳答案 r=StringIO.new("mytext")和here'sthedocumentation. 关于java-Java的StringReader的Ruby等价物是什么?,我们在StackOverflow上找到一个类似的问题: https://st