Kotlin+SpringBoot与Swagger生成API文档详解

1、前言与目的

工程架构,请看我之前撰写的 Kotlin +SpringBoot + MyBatis完美搭建最简洁最酷的前后端分离框架进行完善

  • 使用Swagger开放源码和专业工具集,为用户、团队和企业简化API开发。

  • 简单的来说,Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务的接口文档。

  • 减少开发人员编写API文档,可以通过代码生成器生成API文档。

2、API工具有哪些呢?

  • 这里列举一下常见的工具清单,以及常见比较好用的推荐给大家伙使用
名称 官方 说明
Swagger https://swagger.io/ 推荐
Apidocjs http://apidocjs.com/ 推荐
confluence https://www.atlassian.com/software/confluence 推荐
showdoc https://www.showdoc.cc/
eolinker https://www.eolinker.com/#/
MinDoc https://www.iminho.me/
apizza https://apizza.net/pro/#/
RAP http://rapapi.org/org/index.do
raml https://raml.org/
APIJSON https://jsonapi.org/
  • 为了手机的布局问题,这里提供图片更为直观


    Kotlin+SpringBoot与Swagger生成API文档详解_第1张图片
    API工具.png
  • 由于前三者我都用过,经过开发过程中个人觉得最快生成的文档的是Swagger和Apidocjs,而且confluence需要自己写文档,从时间上的成本会高点,如果说到规范文档这方面肯定它比前2者会更优

  • apidocjs ,这个生成方式需要安装NodeJS,定好模板,然后使用Nodejs命令进行生成相应的接口API文档,只需要懂点简单的语法和规则即可,不需要像Swagger那么麻烦要整合Spring或SpringBoot等方式,然后再打包把工程进行发布前端才可以看到相应的API,然而apidocjs 生成静态的Html即可。

  • 其他无使用过,不发表其他建议。希望通过以上的列表,根据自己的情况进行选择与学习

3、springfox大致原理:

  • springfox的大致原理就是,在项目启动的过种中,spring上下文在初始化的过程,框架自动跟据配置加载一些swagger相关的bean到当前的上下文中,并自动扫描系统中可能需要生成api文档那些类, 并生成相应的信息缓存起来。如果项目MVC控制层用的是springMvc那么会自动扫描所有Controller类,并生成对应的文档描述数据.

  • 该数据是json格式,通过路径:项目地址/ v2/api-docs可以访问到该数据,然后swaggerUI根据这份数据生成相应的文档描述界面。
    因为我们能拿到这份数据,所以我们也可以生成自己的页面.

4、Swagger注解详解

ApiModel 属性

属性名称 数据类型 默认值 说明
value String 类名 为模型提供备用名称
description String "" 提供详细的类描述
parent Class parent Void.class 为模型提供父类以允许描述继承关系
discriminatory String "" 支持模型继承和多态,使用鉴别器的字段的名称,可以断言需要使用哪个子类型
subTypes Class[] {} 从此模型继承的子类型数组
reference String "" 指定对应类型定义的引用,覆盖指定的任何其他元数据
  • 为了手机的布局问题,这里提供图片更为直观


    Kotlin+SpringBoot与Swagger生成API文档详解_第2张图片
    ApiModel .png

ApiModelProperty 属性

属性名称 数据类型 默认值 说明
value String "" 属性简要说明
name String "" 运行覆盖属性的名称。重写属性名称
allowableValues String "" 限制参数可接收的值,三种方法,固定取值,固定范围
access String "" 过滤属性,参阅:io.swagger.core.filter.SwaggerSpecFilter
notes String "" 目前尚未使用
dataType String "" 参数的数据类型,可以是类名或原始数据类型,此值将覆盖从类属性读取的数据类型
required boolean false 是否为必传参数,false:非必传参数; true:必传参数
position int 0 允许在模型中显示排序属性
hidden boolean false 隐藏模型属性,false:不隐藏; true:隐藏
example String "" 属性的示例值
readOnly boolean false 指定模型属性为只读,false:非只读; true:只读
reference String "" 指定对应类型定义的引用,覆盖指定的任何其他元数据
allowEmptyValue boolean false 允许传空值,false:不允许传空值; true:允许传空值
  • 为了手机的布局问题,这里提供图片更为直观


    Kotlin+SpringBoot与Swagger生成API文档详解_第3张图片
    ApiModelProperty .png

5、代码实现

swagger 依赖的jar


  
       io.springfox
       springfox-swagger2
        2.8.0
  
 
    io.springfox
   springfox-swagger-ui
   2.8.0

  • 第一种方式 SwaggerConfiguration 代码实现

/*
  * 访问路径: http://localhost:8080/swagger-ui.html#/
 */
@EnableSwagger2 // 启用Swagger
@Configuration
open class SwaggerConfiguration {
    @Bean
    open fun createRestApi(): Docket {   
         return  Docket(DocumentationType.SWAGGER_2).select()
          .apis(RequestHandlerSelectors
          .withMethodAnnotation(ApiOperation::class.java)).build();
    }
    
}
  • 第二种方式 SwaggerConfiguration 代码实现


/*
  * 访问路径: http://localhost:8080/swagger-ui.html#/
 */
@EnableSwagger2 // 启用Swagger
open class SwaggerConfiguration {

    companion object {
        const val SWAGGER_SCAN_BASE_PACKAGE = "com.flong";
        const val VERSION = "1.0.0";
    }

    @Bean
    open fun createRestApi(): Docket {
        return Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
        .apis(RequestHandlerSelectors.basePackage(SWAGGER_SCAN_BASE_PACKAGE))
          //  api接口包扫描路径
         // 只对restApi路径下面的所有连接请求
        //.paths(regex("/restApi.*"))
        // 可以根据url路径设置哪些请求加入文档,忽略哪些请求
        .paths(PathSelectors.any()) .build();
        
    }

    fun apiInfo(): ApiInfo {
            // 设置文档的标题
        return ApiInfoBuilder().title("Swagger2 接口文档示例")
            // 设置文档的描述->1.Overview
            .description("更多内容请关注:https://github.com/jilongliang")
            // 设置文档的版本信息-> 1.1 Version information
            .version(VERSION)
            .contact("liangjl")
             // 设置文档的License信息->1.3 License information
            .termsOfServiceUrl("www.apache.org")
            .build();
    }
}
  • Model代码配置


/**
 *data保持数据data class就是一个类中只包含一些数据字段,类似于vo,pojo,java bean。一般而言,
 *我们在Java中定义了这个数据类之后要重写一下toString,equals等方法。要生成get,set方法
 *https://www.cnblogs.com/liuliqianxiao/p/7152773.html
 *注意mybatis查询数据,然后封装实体的时候,构造方法这里有点儿坑,查询的字段必须与构造方法一直。
 * 
 * @ApiModelProperty()用于方法,字段; 表示对model属性的说明或者数据操作更改 
 * value–字段说明 
 * name–重写属性名字 
 * dataType–重写属性类型 
 * required–是否必填 
 * example–举例说明
 * hidden–隐藏
 * notes用于提示内容 
 */
@TableName("t_user")
@ApiModel(value = "用户对象", description = "用户对象")
data class User constructor(
    
    @TableId(value = "user_id", type = IdType.ID_WORKER)
    @ApiModelProperty(value = "用户Id",notes = "用户Id")
    var userId: Long?= null,//用户Id主键,IdWork生成
    
    @ApiModelProperty(value = "用户名称",notes ="用户名称")
    var userName: String? = null,//用户名
    
    
    @ApiModelProperty(value = "用户密码",notes ="用户密码")
    var passWord: String? = null,//密码
    
    @TableLogic
    @JSONField(name = "isDeleted")
    @ApiModelProperty(value = "删除状态,0-未删除,1-已删除",notes ="删除状态,0-未删除,1-已删除")
    var isDeleted: Int? = null,//删除
    
    @ApiModelProperty(value = "创建时间",notes ="创建时间")
    var createTime: Date? = null //创建时间,允许为空,让数据库自动生成即可

  ) :Serializable{
 
    //手动重写toString方法
    override fun toString(): String {
        return "[User(userId = $userId,userName = $userName, passWord=$passWord,isDeleted=$isDeleted,createTime=$createTime)]"
    }

    //equals
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as User
        if (userId != other.userId) return false
        if (userName != other.userName) return false
        if (passWord != other.passWord) return false
        if (isDeleted != other.isDeleted) return false
        if (createTime != other.createTime) return false
        return true
    }
    override fun hashCode(): Int {
        var result = userId?.hashCode() ?: 0
        result = 31 * result + (userName?.hashCode() ?: 0)
        result = 31 * result + (passWord?.hashCode() ?: 0)
        result = 31 * result + (isDeleted?.hashCode() ?: 0)
        result = 31 * result + (createTime?.hashCode() ?: 0)
      
        return result
    }

}
  • Controller代码


@RestController
@RequestMapping("/rest")
@Api("用户模块")
open class UserController : BaseController(){
    
    @Autowired private lateinit var userService: UserService
 
    
    @ApiOperation(value="调整页面")
    @GetMapping("/list1")
    fun list1():  String{
        return "NewFile" //跳转页面
    }
    
    //添加
    @ApiOperation(value="添加用户")
    @PostMapping("/add")
    fun add():  Unit{
        userService.addUser()
    }
    
    //删除
    @ApiOperation(value="删除用户")
    @DeleteMapping("/deletedById")
    fun deletedById(userId : Long):  Unit{
        userService.deleteById(userId);
    }
    //更新
    @ApiOperation(value="更新用户")
    @PostMapping("/update")
    fun update(user : User):  Unit{
        userService.updateById(user)
    }
    //根据Id查询用户
    @ApiOperation(value="根据Id查询用户")
    @GetMapping("/getUserId")
    fun getUserId(userId :Long):Any?{
        var user = userService.getUserId(userId);
        if(user ==null){
            var msgCode = UserMsgCode.FIND_NOT_USER;
            throw BaseException(msgCode.code!! ,msgCode.message!!)
        }
        return userService.getUserId(userId)
    }
    
    //查询所有
    @ApiOperation(value="查询所有用户信息")
    @GetMapping("/queryAllUser")
    fun queryAllUser():List?{
        return userService.queryAllUser()
    }
    //分页查询
    @ApiOperation(value="用户列表分页",notes="用户列表分页")
    @GetMapping("listPage")
    fun listPage(query :UserQuery) :PageVO ? {
        var listPage = userService.listPage(query);
        return listPage;
    }
    
}
  • Application 入口启动代码

@EnableAsync
@EnableSwagger2
@Configuration
@EnableScheduling
@EnableAutoConfiguration  //启用读取配置
@ComponentScan("com.flong.kotlin")  //扫描com.flong文件目录下
@SpringBootApplication(scanBasePackages = ["com.flong.kotlin"] )
//@SpringBootApplication(scanBasePackages = arrayOf("com.flong.kotlin")) 这种写法也OK
open class Application :WebMvcConfigurerAdapter{
    //无参数构造方法,为了解决WebMvcConfigurerAdapter类的内部问题,
    constructor()
    
  /**添加swagger-ui的资源文件拦截器的支持addResourceHandlers是
  WebMvcConfigurerAdapter里面的 */
    override fun addResourceHandlers( registry:ResourceHandlerRegistry) {
        // super.addResourceHandlers(registry);
        registry.addResourceHandler("/WEB-INF/views/**").addResourceLocations("/WEB-INF/views/");
        /** 添加swagger-ui的资源文件拦截器的支持 */
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
    
    @Bean
    open fun jspViewResolver(): InternalResourceViewResolver {
        var resolver = InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        return resolver;
    }

    //静态类
    companion object {
        /**启动SpringBoot的主入口.*/
        @JvmStatic fun main(args: Array) {
            //*args的星号表示引用相同类型的变量
            SpringApplication.run(Application::class.java, *args)
        }
    }
}

注意点:
constructor()无参数构造方法,为了解决WebMvcConfigurerAdapter类的内部问题,实现 WebMvcConfigurerAdapter此类主要是为addResourceHandlers了添加加swagger-ui的资源文件拦截器的支持

6、运行接口

  • Kotlin+SpringBoot与Swagger生成API文档详解_第4张图片
    Swagger-UI.png
  • Kotlin+SpringBoot与Swagger生成API文档详解_第5张图片
    Model实体类

7、工程代码

工程代码在本人的GitHub的dev-swagger分支上

8 、总结与建议

1 、以上问题根据搭建 kotlin与Swagger 实际情况进行总结整理,除了技术问题查很多网上资料,通过自身进行学习之后梳理与分享。

2、 在学习过程中也遇到很多困难和疑点,如有问题或误点,望各位老司机多多指出或者提出建议。本人会采纳各种好建议和正确方式不断完善现况,人在成长过程中的需要优质的养料。

3、 希望此文章能帮助各位老铁们更好去了解如何在 kotlin上搭建Swagger,也希望您看了此文档或者通过找资料亲身经历实操学习效果会更好。

备注:此文章属于本人原创,欢迎转载和收藏.

你可能感兴趣的:(Kotlin+SpringBoot与Swagger生成API文档详解)