手把手教你如何玩转Swagger

情景引入

  • 小白:起床,起床,,赶快起床!!!!
  • 我:你又怎么了呢~~好不容易有个假期,都不能消停消停的吗?
  • 小白:我也想你多休息,但是我又遇到问题了,需要你的帮助~
  • 我:说说,你又遇到什么问题了呢?这么火急火燎的 ~ 真烦人
  • 小白:你是知道的,我们平常开发都是有接口开发文档的,但是,前后端处理挺麻烦的,而且更新又不及时,有时候字段改了,又不及时通知,然后调试的时候就出Bug,到最后谁也说不清楚,还引来一堆麻烦事和无用功。
  • 我:对的,接口开发文档是非常必须的,这个可以便于沟通和交流。然后呢?~
  • 小白:我就在想,有没有好的办法,可以方便进行开发文档的交流呢?需求的改变很头疼,开发文档的改变也很头疼的呢。
  • 我:emmmmmmmmmm,这个嘛,容我想想。
  • 我:既然,你有这样的需求的话,那么好的,我就好好给你上一课,那你可要好好的听哦~
  • 小白:小板凳,都搬来了··········

Swagger描述

接口开发文档的那些事

接口开发文档对于我们程序员来说,这是非常普通的东西了,而且在前后端分离来说,则是对于前端和后端人员进行沟通的一大法宝。但是,这也有一定的局限性,就是无法及时的进行更新和交流。对于我们平常来说,我们可以采取的开发文档的形式有:

  1. Word文件:简单,但是开起来真麻烦
  2. redmine:一个开源的项目管理软件,可视化,功能强大,但是界面太花哨,细节很多,无法捕捉到重点
  3. Teambition:很好,但是,需要费用,只能免费使用一定时间
  4. Xmind:一种思维导图的形式,客观性好,但是编写麻烦
  5. Showdoc:这是非常好的软件,可控性也比较强,个人自己用得也比较多。
  6. PostMan: 这不只能进行接口调试,还可以进行接口文档管理的
  7. MinDoc :这个也还行吧。。知道的人很少
  8. RAP : 这个也非常不错,大家可以试试
    基本的话,自己主要用的也就是上面这些啦(还有些其他的,大家可以留言哦)。
    我们可以观察到,上面这些方法,都有一个共性,就是项目和接口文档的管理是分开的,这样实时性肯定会存在问题,另外的话,管理起来也不是非常方便,使用起来也许多多个不同界面的来回管理。
    那么,既然有了这个问题,有什么办法可以解决的呢?当然有的,根据自己的经历吧,我就来讲解一个工具------Swagger

Swagger是啥呢?

总的来说:

  1. Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
  2. 总体目标是使客户端和文件系统作为服务器以同样的速度来更新。
  3. 文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。
  4. Swagger是国外使用非常多的,就类似我们国内一般使用Showdoc一样。
  5. Swagger集成方便,可视化强大
  6. Swagger集接口文档和测试于一体,就类比将postman和showdoc的结合体
    细一点来说:
    Swagger是一组开源项目,其中主要要项目如下:
  • Swagger-tools:提供各种与Swagger进行集成和交互的工具。例如模式检验、Swagger 1.2文档转换成Swagger 2.0文档等功能。
  • Swagger-core: 用于Java/Scala的的Swagger实现。与JAX-RS(Jersey、Resteasy、CXF…)、Servlets和Play框架进行集成。
  • Swagger-js: Swagger通过JavaScript来进行实现。
  • Swagger-node-express: Swagger模块,用于node.js的Express web应用框架
  • Swagger-ui:一个无依赖的HTML、JS和CSS集合,可以为Swagger兼容API动态生成优雅文档。(界面的效果就是这个模块
  • Swagger-codegen:代码生成器,脚手架。可以根据swagger.json或者swagger.yml文件生成指定的计算机语言指定框架的代码。(用得比较少)

开发环境

架构:SpringBoot +Mybatis + Mysql
系统软件:Idea + win7
具体的集成步骤都在我的博文有详细的介绍
https://blog.csdn.net/Cs_hnu_scw/article/details/78961232

开发步骤(小试牛刀)

  1. 搭建基本的SpringBoot+Mybatis+Mysql的环境(这个在开发环境中已经说明)
  2. 引入Swagger的pom依赖
<!--swagger-->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>2.9.2</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>2.9.2</version>
		</dependency>

  
  

    备注:版本的话,自己根据需求来即可,不一定就要用我的版本

    1. 编写Swagger的配置类
      备注:这个类的话,要与springboot的启动类在同级即可
    package com.scw.springboot.swagger.config;
    import io.swagger.annotations.ApiOperation;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.service.ApiInfo;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    /**
     * @ Author     :scw
     * @ Date       :Created in 上午 11:44 2018/12/5 0005
     * @ Description:对于swagger的配置类,@Configuration表示是一个配置类,@EnableSwagger2表示是swagger的配置
     *                ConditionalOnProperty注解是对于是否开启了swagger的灵活配置(这个适合在开发环境中开启,而正式的时候应该关闭)
     * @ Modified By:
     * @Version: 1
     */
    @Configuration
    @EnableSwagger2
    @ConditionalOnProperty(prefix = "swagger", name = "button-open", havingValue = "true")
    public class SwaggerConfig {
        /**
         * 创建获取api应用
         * @return
         */
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .apiInfo(apiInfo())
                    .select()
                    //这里采用包含注解的方式来确定要显示的接口(建议使用这种)
                    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                    //.apis(RequestHandlerSelectors.basePackage("com.scw.springboot.swagger.controller"))    //这里采用包扫描的方式来确定要显示的接口
                    .paths(PathSelectors.any())
                    .build();
        }
        /**
         * 配置swagger文档显示的相关内容标识(信息会显示到swagger页面)
         * @return
         */
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("swagger使用")
                    .description("本人博客地址如下:")
                    .termsOfServiceUrl("https://blog.csdn.net/Cs_hnu_scw")
                    .contact("hnuscw")
                    .version("1.0")
                    .build();
        }
    }
    

      1. 编写application.properties文件
        备注:因为我的swagger的类中是采用了“开关”来控制,是否要进行swagger的处理的,而我的开关的控制就在application.properties文件中,所以,需要稍微编写即可。
      # Swagger相关的配置,这个要与swagger的配置类中设置一样即可,不需要说完全和我写的一样都是可以的哦!!!!
      swagger.button-open = true
      
        
        
        1. 编写controller层代码
          备注:因为Swagger是一种进行前后端文档开发的方便,所以,既然是开发文档的处理,那么很明显就是要在controller层的代码进行相关的处理了,那么如何进行处理的话,就看下面的简单内容。
        package com.scw.springboot.swagger.controller;
        import com.scw.springboot.swagger.model.Person;
        import com.scw.springboot.swagger.service.PersonService;
        import io.swagger.annotations.*;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Controller;
        import org.springframework.ui.Model;
        import org.springframework.web.bind.annotation.*;
        /**
         * @author scw
         * @create 2018-01-03 10:31
         * @desc swagger的牛刀小试
         **/
        @Controller
        @Api(value = "swagger牛刀小试")
        public class PersonController {
            @Autowired
            public PersonService personService;
            /**
             * 根据ID查询数据
             * @param id
             * @return 返回字符串
             */
            @ApiOperation(value = "测试根据id获取用户信息的方法(1)" ,notes="测试查询方法", tags = {"query"})
            @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int")
            @RequestMapping(value = "person/getperson1", method = RequestMethod.GET)
            @ResponseBody
            public String queryPerson1(@RequestParam Integer id ){
                Person person = personService.getPersonById(id);
                return "hello";
            }
        }
        
          
          
          1. 启动我们的SpringBoot项目
            备注:
            springboot的启动方式,我就不多说了,稍微提一下
            (1)main方法的启动,这也是我们开发中用得最多的形式。
            (2)jar包启动,因为springboot是自带maven管理的,所以,可以先用maven打成jar包,然后在控制台(CMD)中运行java 的jar包的命令也是可以。
            (3)war包启动:这种一般是上线的形式,打成一个war包,然后放到服务器tomcat中来进行运行。这个类型第二种方法,只是需要把pom中的jar修改成war格式即可。
            在这里插入图片描述
          2. 访问项目地址
            http://localhost:8080/swagger-ui.html#/
          3. 查看效果
            在这里插入图片描述
          4. 大功告成
            通过上面的这些步骤,我们就可以看到在线的开发文档啦。是不是挺方便的呢?

          项目结构

          在这里插入图片描述

          好好看,这些细节你不能放过

          备注:虽然,我们通过前面的知识,大体的已经对Swagger有了了解,并且也搭了最简单的环境,但是,这都还只是皮毛,我们应该认真的分析里面的内容,便于我们以后集成到我们项目,来方便前后端的交流,所以,下面这些内容你应该都要深度了解。

          出现如下的问题(非常普遍)

          访问链接弹出提示框(如下)

          备注:Swagger Unable to infer base url 问题
          在这里插入图片描述
          **原因:**其实这个是因为Swagger无法对我们要进行扫描的controller进行标识,所以,导致内容无法进行显示,而提示这个URL链接。
          解决方法:
          方法(1)在我们的Controller层的类代码添加Swagger的注解。

          @Api(value = "XXXXX")
          public class PersonController {
          
            
            

            如:
            在这里插入图片描述
            方法二(2)在Swagger的配置类中添加注解@EnableSwagger2
            如:
            在这里插入图片描述

            页面中的测试方法无反应

            描述:在这里插入图片描述
            **原因:**这个的话,我们要保证一点,就是我们先测试了controller的代码是正常的(就是说逻辑不存在问题,这样才保证是由于swagger的某些地方出现问题)。这个点了没反应并不是说没有执行还是什么,而是由于程序出现了问题。具体的原因,就是因为我们测试的方法中的参数类型和设置不一致而导致
            **解决方法:**确保接受参数类型和设置类型一致
            比如:
            在这里插入图片描述
            **描述:**就是大致一看,都是Integer不是一样吗?其实,不是的,在dataType中,我们不需要用包装类型,而是用对应的基本类型即可,所以,Integer使用int,Long 使用long就好了,否则就会发生我描述的这个问题。
            分析:其实这个问题,我也看了很多内容,发现很多人都是用的dataType=Integer这样的博文,但是我无法保证他们是否真正有进行测试过,还有就是不知道是不是版本的原因,因为我用的是2.9的版本(试过其他的版本,我测的都是一样的结果)。所以,我这里建议采取基本类型对应就好,不需要使用包装类型

            为什么一个方法有在页面中有很多测试方法出现

            **描述:**如果所示
            在这里插入图片描述
            **原因:**是由于在代码中没有设置该参数是需要哪一种处理方式,所以swagger就给每一种都设置了处理的形式(哈哈,是不是太友好了点呢!!!)。
            **解决方法:**设置一种接受方式
            如:设置为POST方式

                @RequestMapping(value = "person/getperson4", method = RequestMethod.POST)
            

              Swagger关键注解的使用情况

              备注:Swagger的话,主要就是那么几个注解,但是使用起来,不是很熟悉的话还是容易产生很多问题的,所以,我这里重点分析一下。

              @Api

              使用地点

              作用于类上(类型@controller注解),用于标识是一个Swagger的资源类

              参数

              value :描述说明
              tag:也是描述说明,类型value,个人习惯使用value

              @ApiOperation

              使用地点

              用于方法;表示一个http请求的操作

              参数

              value :对该方法的描述说明
              notes:用于提示内容
              tags:API分组(类似将一个班的同学分多少组)

              实例效果

                  @ApiOperation(value = "测试根据id获取用户信息的方法(1)" ,notes="测试查询方法", tags = {"query"})
              

              • 1
              • 2

              在这里插入图片描述

              @ApiParam

              使用地点

              用于方法,参数,字段说明;表示对参数的添加元数据(说明或是否必填等)

              参数

              name:参数名(中文,英文都可以,用于显示)
              value:参数说明 (需要与接受参数一致)
              required:是否必填

              实例

              public Object queryPerson6( @ApiParam(name = "id",required = true,value = "用户ID")
                                                     @RequestParam Integer id )
              
                
                
              • 1
              • 2

              @ApiImplicitParam

              使用地点

              用于方法,主要对于接受参数的设置

              参数

              name:参数ming
              value:参数说明
              dataType:数据类型
              paramType:参数类型
              example:举例说明
              defaultValue:参数的默认值

              实例

                  @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int")
              
                
                

                @ApiImplicitParams()

                使用地点

                用于方法,包含多个 @ApiImplicitParam

                参数

                无特别参数

                实例

                 @ApiOperation(value = "测试多个参数(5)" ,notes="测试查询方法", tags = {"query"})
                    @ApiImplicitParams({
                            @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int"),
                            @ApiImplicitParam(paramType = "query", name = "userName", value = "用户名", required = true, dataType = "String"),
                    })
                    @RequestMapping(value = "person/getperson5", method = RequestMethod.GET)
                    @ResponseBody
                    public Object queryPerson5( @RequestParam String userName,
                                                @RequestParam Integer id ){
                        return "success";
                    }
                
                  
                  

                  @ApiIgnore

                  使用地方

                  用于类或者方法上,可以不被swagger显示在页面上,不经常用

                  参数

                  value:描述值

                  @ApiModel

                  使用地方

                  用于实体类javabean ;表示对类进行说明,用于参数用实体类接收(当接受的参数是实体的时候,则必须用这个修饰实体类)

                  参数

                  value:表示对象名
                  description:描述
                  其实参数一般都不写

                  实例

                  @ApiModel(value = "用户实体模型")
                  public class Person {
                  
                    
                    

                    @ApiModelProperty

                    使用地方

                    用于方法,字段(实体类中); 表示对model属性的说明或者数据操作更改

                    参数

                    value:字段说明
                    name:重写属性名字
                    dataType:重写属性类型
                    required:是否必填
                    example:举例说明
                    hidden:隐藏

                    实例

                    @ApiModelProperty(value = "用户id", required = true)
                        private Integer id;
                        @ApiModelProperty(value = "用户名", required = true)
                        private String name;
                    
                      
                      

                      在这里插入图片描述

                      多种不同的接受参数的方式

                      备注:我们在方法的接受参数和返回形式中,都会有很多的方式,那么根据不同的效果,我们也会采取不同的处理,所以,这里就主要讲解一下,多种不同的处理方法。

                      返回单纯的String字符串

                      /**
                           * 根据ID查询数据
                           * @param id
                           * @return 返回字符串
                           */
                          @ApiOperation(value = "测试根据id获取用户信息的方法(1)" ,notes="测试查询方法", tags = {"query"})
                          @ApiImplicitParam(paramType = "query", name = "id",
                                       value = "用户Id", required = true, dataType = "Integer")
                          @RequestMapping(value = "person/getperson1", method = RequestMethod.GET)
                          @ResponseBody
                          public String queryPerson1(@RequestParam Integer id ){
                              Person person = personService.getPersonById(id);
                              return "hello";
                          }
                      
                        
                        

                        返回实体Json

                        /**
                             * 根据ID查询数据
                             * @param id
                             * @return 返回实体的json格式
                             */
                            @ApiOperation(value = "测试根据id获取用户信息的方法(2)" ,notes="测试查询方法", tags = {"query"})
                            @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int")
                            @RequestMapping(value = "person/getperson2", method = RequestMethod.GET)
                            @ResponseBody
                            public Object queryPerson2(@RequestParam Integer id , Model model){
                                Person person = personService.getPersonById(id);
                                model.addAttribute("person" , person);
                                return person;
                            }
                        
                          
                          

                          返回页面格式

                          /**
                               * 根据id获取用户信息
                               * @param id
                               * @param model
                               * @return 返回页面
                               */
                              @ApiOperation(value = "测试根据id获取用户信息的方法(3)" ,notes="测试查询方法", tags = {"query"})
                              @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int")
                              @RequestMapping(value = "person/getperson3", method = RequestMethod.GET)
                              public Object queryPerson3(@RequestParam Integer id , Model model){
                                  Person person = personService.getPersonById(id);
                                  model.addAttribute("person" , person);
                                  return "ok";
                              }
                          
                            
                            

                            接受参数是一个

                            可以看返回是单纯的Stirng类型的示例

                            接受参数是多个

                            /**
                                 *  多个参数的形式,利用
                                 * @param userName
                                 * @param id
                                 * @return
                                 */
                                @ApiOperation(value = "测试多个参数(5)" ,notes="测试查询方法", tags = {"query"})
                                @ApiImplicitParams({
                                        @ApiImplicitParam(paramType = "query", name = "id", value = "用户Id", required = true, dataType = "int"),
                                        @ApiImplicitParam(paramType = "query", name = "userName", value = "用户名", required = true, dataType = "String"),
                                })
                                @RequestMapping(value = "person/getperson5", method = RequestMethod.GET)
                                @ResponseBody
                                public Object queryPerson5( @RequestParam String userName,
                                                            @RequestParam Integer id ){
                                    return "success";
                                }
                            
                              
                              

                              接受参数是实体类

                              步骤:
                              (1)找到对应的实体类
                              (2)在对应的字段和类上添加注解

                              package com.scw.springboot.swagger.model;
                              import io.swagger.annotations.ApiModel;
                              import io.swagger.annotations.ApiModelProperty;
                              /**
                               * @author scw
                               * @create 2018-01-03 10:33
                               * @desc 对应数据库中的实体类
                               **/
                              @ApiModel(value = "用户实体模型")
                              public class Person {
                                  @ApiModelProperty(value = "用户id", required = true)
                                  private Integer id;
                                  @ApiModelProperty(value = "用户名", required = true)
                                  private String name;
                                  public Integer getId() {
                                      return id;
                                  }
                                  public void setId(Integer id) {
                                      this.id = id;
                                  }
                                  public String getName() {
                                      return name;
                                  }
                                  public void setName(String name) {
                                      this.name = name;
                                  }
                                  @Override
                                  public String toString() {
                                      return "Person{" +
                                              "id=" + id +
                                              ", name='" + name + '\'' +
                                              '}';
                                  }
                              }
                              

                                (3)在controller层的方法使用注解@RequestBody进行接受处理

                                /**
                                     *  接受的参数是person实体(这种情况用得最多,比如form表单提交)
                                     * @param person
                                     * @return
                                     */
                                    @ApiOperation(value = "测试接受参数为对象实体(4)" ,notes="2222", tags = {"query"})
                                    @RequestMapping(value = "person/getperson4", method = RequestMethod.POST)
                                    @ResponseBody
                                    public String queryPerson4(@RequestBody Person person){
                                        return "成功";
                                    }
                                
                                  
                                  

                                  总结

                                  Swagger还是非常实用的,便于前后端的分离各自的开发,并且集成也非常的容易和快速,并且可控性也比较强(比如,什么时候需要使用和时候地方需要使用)。不过,个人觉得的话,有一点就是一个最大的痛点,就是针对程序中的代码可能看起来比较冗余,因为这个在开发环境是需要的,但是以后正式环境,一般这个功能是很少有的(当然,像SQL监控这些功能还是有的,一般都是超级管理员可以看到,客户是压根不知道,只是为了分析整个系统的性能),所以,代码看起来可能会相对比较多。万事有好,必有坏,个人还是喜欢的,所以,根据大家各自的需求来进行得与舍就行了。。

                                  感谢大家的阅读

                                  猜你喜欢

                                  转载自blog.csdn.net/Sophisticated_/article/details/84979834