偏头痛杨的springfox-swagger自动接口文档生成装置入门

版权声明:偏头痛杨:本文为博主原创文章,未经作者允许不得转载,版权必究! https://blog.csdn.net/piantoutongyang/article/details/79959725
前戏
作为后端程序员的我们,通常我们要构建API服务,要写服务端接口,自然少不了写接口文档,
否则就要口述,但口述的痛点也很多。

一般情况下,我们的接口文档要写两份:
1.给前端团队看的 word 接口文档。
2.给后端团队看的写在代码中的注释。

一样的事情做了两遍,让后端程序员不爽, 繁琐且无趣,稍有遗漏就会造成接口文档与接口不同步的情况,
为引发线上bug埋下伏笔。

那么有没有一种解决方案可以解决写两次的痛点呢?目前市面上大致有两种方案:
1.小幺鸡、sosoapi等:需要维护代码和文档两个地方,不过脱离项目,功能比较多,适合外部文档解决方案。
2.swagger:注解生成,不用维护两套,功能比较单一,依赖项目, 有少量代码入侵,适合内部文档解决方案。


什么是Swagger?
Swagger是一款RESTFUL接口的文档在线 自动生成+功能测试功能软件。
Swagger可以通过代码&注释自动生成漂亮的在线API页面,甚至可以提供运行示例并实时同步。
Swagger会扫描配置与注解自动生成一份json数据,而swagger官方也提供了swagger-ui来做通常的展示。
 

使用方法

注意:
~swagger2.5必须需要spring4的支持,否则会报错。
~如果有/src/test/java的配置文件,则需要同步修改,否则会启动报错。

1.在项目的pom.xml中添加依赖
<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger2</artifactId>
  <version>2.5.0</version>
</dependency>

<dependency>
  <groupId>io.springfox</groupId>
  <artifactId>springfox-swagger-ui</artifactId>
  <version>2.5.0</version>
</dependency>

2.编写swagger的配置类(唯一的一个)
/**
* Title: swagger核心配置类(就这一个).<br>
* Description: MySwaggerConfig<br>
*/
@Configuration
@EnableSwagger2
public class MySwaggerConfig {
    /**
     * Title: 构建swagger.<br>
     * Description: 这个方法起什么名字都可以,主要是注解起作用<br>
     *
     * @category buildDocket
     * @return
     */
    @Bean
    public Docket buildDocket() {
        String env = "dev";//判断当前环境是什么,通过缓存拿。
        Docket docket = null;
        if("dev".equals(env)){
            // 添加http请求头
            ParameterBuilder tokenPar = new ParameterBuilder();
            List<Parameter> pars = new ArrayList<Parameter>();
            tokenPar.name("token").description("令牌").modelRef(new ModelRef("string")).parameterType("header")
                    .required(false).build();
            pars.add(tokenPar.build());

            docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(buildApiInf())// 构建信息
                    .select() // 选择那些路径和api会生成document
                    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))//只扫描有注解的
                    //.apis(RequestHandlerSelectors.any()) 扫描所有类
                    //.apis(RequestHandlerSelectors.basePackage("example.web.controller"))//要扫描的API(Controller)基础包
                    .paths(PathSelectors.any()) // 对所有路径进行监控
                    .build().globalOperationParameters(pars);// 添加header
        }
        
        return docket;
        
    }

    private ApiInfo buildApiInf() {
        String description = "aaaa<br>bbbb<br>通用返回码都放在这里,可以使用便利枚举类的形式形成一个字符串放到这里";
        return new ApiInfoBuilder().title("xxxx项目的API文档").description(description)
                .contact(new Contact("wesley", " http://blog.csdn.net/piantoutongyang ", " [email protected] "))
                .version("2.0.0").build();
    }
}


3.spring配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- Application context definition for PetClinic on JDBC. -->
<beans xmlns="http://www.springframework.org/schema/beans"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

       <bean class="com.swagger.doc.MySwaggerConfig" />
</beans>

4.spring-mvc配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- Application context definition for PetClinic on JDBC. -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <mvc:annotation-driven/>

    <context:component-scan base-package="com.swagger.doc"/>

    <mvc:default-servlet-handler/>
</beans>


swagger注解解释
注解
描述
@Api
标记在类名上,表示这个类是一个swagger资源,只有标注了这个标签相应的api信息才能展现出来,
其中包括可以设置:
description:描述Controller类的作用。  

@ApiOperation
标记&描述具体的方法。一个@API下,可以有多个@ApiOperation
(一个类中有多个方法),
其中包括可以设置:
value:接口名称
notes:接口描述,换行用</br> HMLT的标签就行了
 
@ApiResponses
在一个@ApiOperation下,可以通过@ApiResponses描述API操作可能出现的异常情况。
@ApiResponses用来组合@ApiResponse,暂无用。
@ApiResponse
返回值,该类主要有code值与message两个属性,
code值必须是http 返回code值,默认会有200,404等,不能单独使用,
必须和@ApiResponses一起使用。暂无用。swagger会根据返回类型与返回值自动生成。

@ApiParam
用于描述该API操作接收的参数类型,value用于描述参数,
required指明参数是否为必须。
定义具体的请求参数,类似@RequestParam参数,直接在方法参数上使用。
@ApiImplicitParams
组合一组@ApiImplicitParam,跟ApiParam类似,但他是写在方法的上面,
而不是形参的前面,要更复杂一些。
无法描述较复杂类型的数据对象,建议还是用ApiParam。
@ApiImplicitParam
描述具体的请求参数,比如具体的响应方法的参数为HttpServletRequest时,
我会从该参数中取一些请求参数,
则可以通过该方法单独定义参数.见下面的具体说明,该参数不能单独使用,
必须和@ApiImplicitParams一起使用。
使用ApiImplicitParam时,需要指定paramType,
这样也便于swagger ui 生成参数的输入格式。
paramType有五个可选值 : path, query, body, header, form。
@ApiModel
提供对请求参数与返回结果中的model的定义,实体bean需要自动生成注解。
@ApiModelProperty
提供对请求参数与返回结果中的model的属性定义,可以使用example做默认值,实体bean需要自动生成注解。
@ApiIgnore
使用这个注解忽略这个接口


一个例子

Controller
@Api(description = "controller的中文解释")
@Controller
public class YangController {

    @ResponseBody
     @ApiOperation(value = "接口名称-增111", notes = "接口描述-增,作者:alex")
    @RequestMapping(value = "/yang", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<Object> insertYang( @ApiParam(value = "yang参数", required = true) @RequestBody Yang yang) {
        System.out.println("正在创建111");
        System.out.println(yang.getYangAge());
        System.out.println(yang.getYangId());
        System.out.println(yang.getYangName());

        return new CommonJsonObject<Object>();
    }

    @ApiOperation(value = "接口名称-删", notes = "接口描述-删,作者:alex")
    @ResponseBody
    @RequestMapping(value = "/yang", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<Object> deleteYang(
            @ApiParam(name = "yangId", required = true, value = "杨Id") String yangId) {
        System.out.println("正在删除");
        System.out.println(yangId);

        return new CommonJsonObject<Object>();
    }

    @ApiOperation(value = "接口名称-改", notes = "接口描述-改,作者:alex")
    @ResponseBody
    @RequestMapping(value = "/yang", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<Object> updateYang(@ApiParam(value = "yang参数", required = true) @RequestBody Yang yang) {
        System.out.println("正在修改");
        System.out.println(yang.getYangAge());
        System.out.println(yang.getYangId());
        System.out.println(yang.getYangName());
        return new CommonJsonObject<Object>();
    }
    
    @ResponseBody
    @ApiOperation(value = "接口名称-查单个", notes = "接口描述-查单个,作者:alex")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "yangId", value = "杨Id", required = true, dataType = "String",paramType = "path"),
        @ApiImplicitParam(name = "demoId", value = "做测试用的没有意义,多参数", required = false, dataType = "String",paramType = "query")
    })
    @RequestMapping(value = "/yang/{yangId}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<Yang> findYangById(@PathVariable String yangId,String demoId) {
        System.out.println("正在查询");
        System.out.println(yangId);
        
        Yang yang = new Yang();
        yang.setYangAge(123);
        yang.setYangId(yangId);
        yang.setYangName("hahaha");
        
        CommonJsonObject<Yang> json = new CommonJsonObject<Yang>();
        json.setData(yang);

        return json;
    }
    
    
    @ResponseBody
    @ApiOperation(value = "接口名称-查列表", notes = "接口描述-查列表,作者:alex")
    @RequestMapping(value = "/yangs", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<List<Yang>> findYangList() {
        System.out.println("正在查询xxxxxxxx");
        
        Yang yang1 = new Yang();
        yang1.setYangAge(123);
        yang1.setYangId("123");
        yang1.setYangName("hahaha");
        
        Yang yang2 = new Yang();
        yang2.setYangAge(123);
        yang2.setYangId("123");
        yang2.setYangName("hahaha");
        
        List<Yang> yangList = new ArrayList<Yang>();
        yangList.add(yang1);
        yangList.add(yang2);
        
        CommonJsonObject<List<Yang>> json = new CommonJsonObject<List<Yang>>();
        json.setData(yangList);

        return json;
    }
    
    @ResponseBody
    @RequestMapping(value = "/noshow", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public CommonJsonObject<List<Yang>> noshow() {
        /**
         * 没有没有写@ApiOperation标签则在swagger-ui中显示
         */
        System.out.println("正在查询");
        
        Yang yang1 = new Yang();
        yang1.setYangAge(123);
        yang1.setYangId("123");
        yang1.setYangName("hahaha");
        
        Yang yang2 = new Yang();
        yang2.setYangAge(123);
        yang2.setYangId("123");
        yang2.setYangName("hahaha");
        
        List<Yang> yangList = new ArrayList<Yang>();
        yangList.add(yang1);
        yangList.add(yang2);
        
        CommonJsonObject<List<Yang>> json = new CommonJsonObject<List<Yang>>();
        json.setData(yangList);

        return json;
    }
}


通用返回对象, 必须要用泛型,否则swagger不认你的嵌套对象,无法解析其中文含义。
@ApiModel(value="CommonJsonObject(通用返回模型)")
public class CommonJsonObject <T> {
      // 默认200为应答成功
      @ApiModelProperty(value="返回码",required=true,example="200")
      private int code = 200;
      @ApiModelProperty(value="返回数据",required=true)
      private T data;
      @ApiModelProperty(value="返回描述",required=true)
      private String msg;

      public CommonJsonObject(){

      }

      public CommonJsonObject(int code) {
            this.code = code;
      }

      public CommonJsonObject(int code, T data) {
            this.data = data;
            this.code = code;
      }

      public CommonJsonObject(int code, T data, String msg) {
            this.data = data;
            this.code = code;
            this.msg = msg;
      }

      public int getCode() {
            return code;
      }

      public void setCode(int code) {
            this.code = code;
      }

      public T getData() {
            return data;
      }

      public void setData(T data) {
            this.data = data;
      }

      public String getMsg() {
            return msg;
      }

      public void setMsg(String msg) {
            this.msg = msg;
      }

}

参数bean
import io.swagger.annotations.ApiModelProperty;

@ApiModel(value="Yang(杨模型)")
public class Yang {
       @ApiModelProperty(value="杨ID",required=true,example="12345")
    private String yangId;
      @ApiModelProperty(value="杨用户名",required=true,example="abcde")
    private String yangName;
      @ApiModelProperty(value="杨年龄",required=false,example="16")
    private Integer yangAge;

      public String getYangId() {
            return yangId;
      }
      public void setYangId(String yangId) {
            this.yangId = yangId;
      }
      public String getYangName() {
            return yangName;
      }
      public void setYangName(String yangName) {
            this.yangName = yangName;
      }
      public Integer getYangAge() {
            return yangAge;
      }
      public void setYangAge(Integer yangAge) {
            this.yangAge = yangAge;
      }
}

启动服务后调用这个地址访问接口文档:
http://localhost:xxxx/swagger-ui.html

可以获得生成上述文档的Swagger JSON定义:
http://localhost:xxxx/v2/api-docs



注意事项
1.要注意安全性,在生产环境的外网下是必须不能被访问到swagger-ui.html的。

2.swagger如何能自动生成&更新mock数据呢?
-目前得知是需要后端开发人员自己return或者写example,总之要手写,不能做到自动生成mock数据。
-如果必须是这样,那么前端可以去掉之前的mock.js流程,直接调用后端的假接口
(没有实现逻辑的接口,只提供假数据), 那这些假接口就是前后端的“契约”。
如果后端的接口发生变化,前端第一时间会知道,促使一次前后端沟通,其他的解决方案以后再看。

(如果能做到自动生成mock数据,则前端依然可以玩他的那套mock.js,swagger来提供json格式的mock数据)

3.把功能相似的接口抽成一个controller,不要超过600行,自己划分不同的controller。

4.在开发的过程中,如果前后端有任意一方需要修改数据结构等造成接口变化,需要第一时间通知对方, 
以及通知测试。测试的测试用例有可能会改,不要拖着,否则问题会在前后端集成时彻底爆发。

5.前端也要有自己的单元测试用例,以及构建前端项目时,自动跑测试用例,调用后端接口。
这样接口发生变动后会第一时间爆发,而不是拖到生产环境。

猜你喜欢

转载自blog.csdn.net/piantoutongyang/article/details/79959725