开端

Everybody,好久不见,因为开学的原因,已经拖更了半个多月。周末总算是闲下来写写博文,主要还是不想学习学校里教的那些东西。

我们都知道,API 接口是一个非常神圣的内容。你做的好,前后端开发人员情同手足,和和美美;你做的不好,说不定他们每天一见面就要吵。在现在这个阶段,大家更倾向于使用一些已有的框架来进行API文档的开发,这样更加方便管理。尤其是动态更新 API 文档,更加省去了后端程序员一大部分的工作。在《阿里巴巴 Java 开发手册》中写到,API 文档和 Javadoc 就是为了方便他人阅读和理解程序而设计的,后端程序员若是更改了源代码而没有对应修改文档或注释,那文档和注释就没有了他们存在的意义。

所以今天,我就来讲一个快速生成 API 文档的框架——Swagger。没错,他就是那么 Swag!以及如何整合到项目中去,包含了后端的注解生成器与前端嵌入式的页面显示方案。话不多说,那我们就开始吧。

补充:实践过程中遇到特殊问题,请前往下方链接下载 dist 文件夹的内容并导入至项目文件中

->Github Swagger-UI

介绍

为了解决前面讲到的问题,人们就提出了各种解决方案,方案用的人一多,就自成了一套体系,成了一套规范,这就是 Swagger 的由来。

按照新的开发模式,只要每次更新 Swagger 的描述文件,就可以自动生成接口文档。但即便如此,仍旧需要开发人员每次修改 yml 或 json 文件。人都是有惰性的,久而久之就再一次忽略了描述文件的修改,导致更新不同步。因此急需更加简便的方案。

所以作为Java届服务端的大一统框架Spring,迅速将Swagger规范纳入自身的标准,建立了 Spring-swagger 项目,后面改成了现在的 Springfox。在项目中引入 Springfox 之后,可以扫描代码并生成描述文件,同步接口文档,实现自动化管理。就目前来说已经是最重要且高效的几个生成接口文档的方式之一了。

->点击链接了解Swagger

截屏2020-09-19 下午1.36.18

整合

介绍不多说,直接开始整合。关于阿里云仓库的使用我就粘贴我的另一篇文章中的内容,免得大家频繁跳转页面。

使用阿里云仓库

众所周知,如果你从 github clone 一个项目,如果没有梯子下载 jar 包可能就要花几个小时的时间。还好阿里爸爸给我们提供了镜像仓库,几分钟就能解决 jar 包的问题。关于引用仓库可以在 maven 配置中修改:

<mirror>
  <id>aliyunmaven</id>
  <mirrorOf>*</mirrorOf>
  <name>阿里云公共仓库</name>
  <url>https://maven.aliyun.com/repository/public</url>
</mirror>

不过我还是推荐大家在自己的项目中加入仓库,灵活性更高:

<repositories>
    <repository>
        <id>alimaven</id>
        <name>aliyun maven</name>
        <url>https://maven.aliyun.com/repository/public</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

这里我就直接用 public 仓库,这个仓库整合了两个小仓库,东西更丰富些。releases 是线上版本,snapshots 是开发版本,都填 true 问题不大。

搜索并导入 jar 包

->阿里云仓库

大家可以在上边这个网址内搜索自己要找的 jar 包,中心 central 仓库够大了,输入文件名,点击搜索到的蓝色文件名后把 Maven 依赖这些代码复制进项目里即可。

截屏2020-09-19 下午1.43.43

像下面的一样,我们引入最新版本的 Swagger 相关的 jar 包,如需拓展可以引用其他包。

<!--核心包-->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>
<!--默认ui包-->
<dependency>
    <groupId>io.springfox</groupId>-->
    <artifactId>springfox-swagger-ui</artifactId>-->
    <version>3.0.0</version>-->
</dependency>
<!--推荐ui包-->
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>swagger-bootstrap-ui</artifactId>
    <version>1.9.6</version>
</dependency>

由于默认的 UI 界面并不那么舒适,也不适合用于前端展示,所以推荐使用最后一个包,两者选其一导入。

这里提一点,项目使用的 Java 版本最好是高于或等于 11,如果太低下面讲到的一步中可能会报错。

创建SpringFoxConfig配置类

我们先来写配置,再使用注解优化显示。在后端项目中创建一个配置类如下:

@Configuration
@EnableSwagger2
public class SpringFoxConfig {
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .host("localhost:9988")
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.kevin.ims.controller"))
                .paths(Predicate.not(PathSelectors.regex("/error.*")))
                .paths(PathSelectors.regex("/.*"))
                .build();
    }
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("接口文档")
                .contact(new Contact("Kevin", "http://test.ims.cool", "jiazekai1003@gmail.com"))
                .description("Swagger动态接口文档")
                .license("The Apache License, Version 2.0")
                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
                .version("Demo")
                .build();
    }
}

特别提醒,类名前一定要写两个注解。@Configuration表示这是受 Spring 控制的配置组件,@EnableSwagger2表示启用 Swagger 2。

首先构建一个 docket。host 填写后端项目的 url。apis 填写需要扫描的包。paths中可以选择扫描路径以及不扫描的路径,推荐将 error 排除。

然后构建一个 apiInfo,里面的信息自行填写,按我的模板改一下即可,不影响实际使用。

通过注解实现文档优化

如果看成果,可以跳过这一部分,但是优化之后的文档更加美观写易于阅读。

先从底层的 Entity 开始写:

@Data
@ApiModel(description = "基本实体")
public abstract class BaseEntity implements Serializable {
    @ApiModelProperty(value = "创建时间", required = true)
    protected long utcCreate;

    @ApiModelProperty(value = "修改时间", required = true)
    protected long utcModify;

    @ApiModelProperty(value = "修改人", required = true)
    protected String modifyBy;

    @ApiModelProperty(value = "是否有效", required = true)
    protected Boolean valid;

    @ApiModelProperty(value = "备注", required = true)
    protected String remark;
}

使用@ApiModel注释实体类的别名,使用@APIModelProperty注释实体类属性的别名和是否必要等信息。

不要忘了把 VO 类也注解一遍。

然后注解 Controller 层的类:

@Api(tags = "用户模块")
@CrossOrigin
@RestController()
@RequestMapping("/user/")
public class UserController extends BaseController {

    @Resource(name = "userServiceImpl")
    private UserService userService;

    @Resource(name = "roleServiceImpl")
    private RoleService roleService;

    @ApiOperation(value = "默认Get请求", notes = "请求不存在的路径时调用此默认接口")
    @ApiImplicitParam(name = "name", value = "默认字段", required = true, dataTypeClass = String.class)
    @GetMapping(value = "/{name}")
    @ResponseBody
    public String helloWorld(@PathVariable(name = "name") String name) {
        return "Hello " + name;
    }

    @ApiOperation(value = "添加用户", notes = "插入一条用户记录")
    @ApiImplicitParam(name = "usrType", value = "用户角色", required = true, dataTypeClass = String.class)
    @RequiresRoles("admin")
    @RequiresPermissions("user:insert")
    @PostMapping(value = "/insert")
    @ResponseBody
    public Response<User> saveUserInfo(@NotNull @RequestBody User sysUser, @RequestParam String usrType) {
        User result = userService.saveUserInfo(sysUser);
        if (result != null) {
            roleService.createUserRole(usrType, result.getUsrName());
            return getSuccessResult(result);
        }
        return getFailResult(405, "Message already exist!");
    }
}

使用@APi标记类,并取别名。

使用@ApiOperation备注接口,可以加上 note 写更多描述信息。

使用@ApiImplicitParam备注每一个接口中所需要的参数,注意如果参数为先前已经注解过的实体则不需要再次备注,如果参数为多个可以在@ApiImplicitParams中内嵌@ApiImplicitParam。具体的填写格式如下:

name:参数名

value:参数的汉字说明、解释

required:参数是否必须传

paramType:参数放在哪个地方

· header --> 请求参数的获取:@RequestHeader

· query --> 请求参数的获取:@RequestParam

· path(用于restful接口)--> 请求参数的获取:@PathVariable

· body(不常用)

· form(不常用)

dataType:参数类型,默认String,其它值dataType="Integer"

defaultValue:参数的默认值

前端内嵌网页访问文档

至此简单的优化已经完成了,如果项目工程比较大,还可以将不同的 Controller 放入不同的 Docket 中。

那么前端我们的目的就是访问先前配置的路径下的接口文档,先前 docket 中我们设置的 host 为 localhost:9988,那么访问 localhost:9988/doc.html 就可以看到接口文档了,如果没有使用推荐的 UI 包,则需要访问 swagger-ui.html。

截屏2020-09-19 下午3.02.34

可以看到已经有那味儿了,为了能让文档更符合我们项目的结构,更方便查阅,可以采用嵌入式的方法。在前端页面合适的组件内,使用<iframe>标签内嵌显示文档。

<iframe src="http://test.ims.cool:9988/doc.html" width="100%" height="95%"/>

接下来我们在项目页面中直接打开文档,就可以看到内嵌效果了(我事先已经做好了一个新组件并注册在了菜单中)。

截屏2020-09-19 下午3.06.58

可以发现,通过优化注解,中文版的 API 文档更加方便理解,让人一目了然。

总结

本文简单介绍了一下 Swagger 的由来,从后端导包、配置、注解,到前端内嵌网页显示文档,教大家如何快速整合这一框架。在实际测试中,在访问 UI 页面时遇到过问题,采用下载官方 GitHub 的 Dist 文件夹方法解决了,如果有更好的方法比如采用前端导入的方式,请大佬们留言教教我。另外,跨域问题相信也会有一部分朋友遇到,请查阅我之前写过的文章一并解决。希望本文能帮助大家对 Swagger2 和如何整合有一个初步的概念。篇幅有限,深入的知识就只能自行学习了。

最后修改:2022 年 05 月 27 日
随意