swagger2和spring boot整合构建RESTful API文档


参考文章:http://blog.didispace.com/springbootswagger2/

这里在这篇文章的基础上加了自己的一些理解和一些没有讲到,但是常用的功能

由于Spring Boot能够快速开发、便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。

这样一来,我们的RESTful API就有可能要面对多个开发人员或多个开发团队:IOS开发、Android开发或是Web开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:

  • 由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
  • 随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。

为了解决上面这样的问题,本文将介绍RESTful API的重磅好伙伴Swagger2,它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API。

1. 添加Swagger2依赖

pom.xml中加入Swagger2的依赖坐标

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

2. 建立Swagger2配置类

注意这个配置类需要放到Application.java所在的同一目录或者子目录下

@SpringBootConfiguration
public class Swagger2Config {

    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.itcrud.swagger"))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(parameterBuilder());
    }

    //API的基本信息
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("IT-CRUD接口文档")
                .description("专注于学习笔记,想和我一起学习,请关注博客:http://blog.itcrud.com")
                .termsOfServiceUrl("http://blog.itcrud.com")
                .contact("IT-CRUD")
                .version("1.0.0")
                .build();
    }

    //请求参数构建
    private List<Parameter> parameterBuilder() {
        List<Parameter> parameters = Lists.newArrayList();
        parameters.add(new ParameterBuilder().name("token").description("登录验证")
                .modelRef(new ModelRef("string")).required(false)
                .parameterType("header").build());
        return parameters;
    }
}

通过createRestApi函数创建Docket的Bean之后,apiInfo()用来创建该Api的基本信息(这些基本信息会展现在文档页面中)。select()函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现,本例采用指定扫描的包路径来定义,Swagger会扫描该包下所有Controller定义的API,并产生文档内容(除了被@ApiIgnore指定的请求)。另外一般项目可能都会用到请求头信息,这里通过ParameterBuilder构建头信息部分,添加到全局选项参数globalOperationParameters中。

3. 添加文档内容

3.1 常用注解

在完成了上述配置后,其实已经可以生成文档内容,但是这样的文档主要针对请求本身,而描述主要来源于函数等命名产生,对用户并不友好,我们通常需要自己增加一些说明来丰富文档内容。需要介绍几个常用的注解。

  • @Api:放在Controller类上面,对类做整体的介绍,主要使用descriptionvalue属性
  • @ApiOperation:放在接口方法上,用来描述方法的信息,常用属性是valuenotes
  • @ApiModel:放在接收请求参数类或者响应参数类上,用来描述类信息,常用属性是value
  • @ApiModelProperty:方法接收请求参数类或者响应参数类的属性字段上,用来描述字段信息,常用属性是value
  • @ApiImplicitParam:放在接口方法上,用来描述参数信息,常用属性有namevaluerequireddataTypeparamType(此注解不推荐用)
  • @ApiImplicitParams:放在接口方法上,用来描述参数信息,描述多个字段信息,其内部属性就一个,是@ApiImplicitParam数组,所以常用属性也是namevaluerequireddataTypeparamType(此注解不推荐用)

3.2 Controller代码

这是个Controller类,里面包含了上面的所有注解。

@Api(description = "UserController", value = "用户服务接口")
@Controller
@RequestMapping("/user")
public class UserController {

    @ApiOperation("添加用户信息")
    @PostMapping("/addUser")
    @ResponseBody
    public String addUser(@RequestBody UserReqDTO reqDTO) {
        return "";
    }

    @ApiOperation("删除用户信息")
    @DeleteMapping("/deleteUser")
    @ResponseBody
    public String deleteUser(@RequestParam(name = "userId") Long userId) {
        return "";
    }

    @ApiOperation("编辑用户信息")
    @PutMapping("/editUser")
    @ResponseBody
    public String editUser(@RequestBody UserReqDTO reqDTO) {
        return "";
    }

    @ApiOperation(value = "查询用户信息", notes = "查询用户信息")
    @GetMapping("/getUser")
    @ResponseBody
    public List<User> getUser(@ModelAttribute UserSearchReqDTO reqDTO) {
        return Lists.newArrayList();
    }

    @ApiOperation(value = "测试header信息", notes = "测试swagger配置中添加的header参数token在传入的时候是否在头信息里面")
    @GetMapping("/testHeader")
    @ResponseBody
    public String testHeader(HttpServletRequest request) {
        System.out.println("header-->" + request.getHeader("token"));
        return "";
    }

    @ApiOperation(value = "模糊查询用户", notes = "根据手机号、用户名模糊查询用户信息")
    @GetMapping("/getUserByKeywords")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "userName", value = "用户名", required = true, dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userPhone", value = "手机号码", required = true, dataType = "String", paramType = "query")
    })
    @ResponseBody
    public List<User> getUsers(@RequestParam("userName") String userName, @RequestParam("userPhone") String userPhone) {
        return Lists.newArrayList();
    }
}

3.3 涉及的实体类

实体类包含请求的参数类和响应的参数类。

@Data
@ApiModel(value = "用户详细信息")
public class User {
    @ApiModelProperty(value = "用户ID")
    private Long id;
    @ApiModelProperty(value = "用户姓名")
    private String userName;
    @ApiModelProperty(value = "用户手机号码")
    private String userPhone;
    @ApiModelProperty(value = "用户性别")
    private Integer gender;
    @ApiModelProperty(value = "用户年龄")
    private Integer age;
    @ApiModelProperty(value = "注册时间")
    private Date registerTime;
}
//-----------------------
@Data
@ApiModel(value = "用户参数类")
public class UserReqDTO {
    @ApiModelProperty(value = "用户ID")
    private Long id;
    @ApiModelProperty(value = "用户姓名")
    private String userName;
    @ApiModelProperty(value = "用户手机号码")
    private String userPhone;
    @ApiModelProperty(value = "用户性别")
    private Integer gender;
    @ApiModelProperty(value = "用户年龄")
    private Integer age;
}
//-----------------------
@Data
@ApiModel(value = "用户查询类")
public class UserSearchReqDTO {
    @ApiModelProperty(value = "用户编号")
    private Long id;
    @ApiModelProperty(value = "查询关键字(用户名+手机号模糊查询)")
    private String keywords;
}

完成上面的代码就可以启动这个springboot项目了。然后直接访问:http://localhost:8080/swagger-ui.html。这个地址根据实际项目不同,但是都是`swagger-ui.html`结尾。

得到的结果如下:

上面的图是整体页面的数据,再展示一下具体接口内部的细节图。

上面细节图中的描述信息、字段名称、说明文字等对应到注解上的属性可以对照看一下,不多解释啦。另外要解释一下上面说@ApiImplicitParams@ApiImplicitParam不推荐用的原因。

3.4 不推荐使用@ApiImplicitParams@ApiImplicitParam原因

  • 当我们接收参数是对象的时候,使用@ApiImplicitParam和参数对象类上的@ApiModel系列的注解有兼容性问题,会打乱swagger上的显示,如下两张对比图:

图一

![](https://raw.githubusercontent.com/itcrud/images/main/images/69C8CA19963E4FCDA5ECEBE394B4BB61.png)

图二

对比图一和图二,图一是不使用`@ApiImplicitParam`的效果,图二是和`@ApiModel`混用的效果。很明显的看出来,不使用`@ApiImplicitParam`更优。这里的请求类`UserReqDTO`类的参数不多,如果多的话有没有自动填充的效果,会是一件很痛苦的事。
  • 当用@ApiImplicitParam注解来标识参数的时候,需要注明此参数的类型,也就是paramType必填,如果不填很可能导致请求参数不能传入,出现问题。看下面的两张对比图:

图三

![](https://raw.githubusercontent.com/itcrud/images/main/images/12F71DCD57EE4DDD9E57505545C5A893.png)

图四

这两个图是请求同一个接口(GET请求),图三请求的时候,该接口的`@ApiImplicitParam`注解将`paramType`值设置为了`query`,图四是将这个属性去掉,默认的`paramType`就是`body`。很容易就会出错。那么应该怎么设置这个类型呢?有哪些类型?下面看一下:
  1. path:参数是链接上的占位符,如:http://blog.itcrud.com/api/getUserById/{id},id参数对应的类型就是path
  2. query:get请求,拼在地址后面的。如http://blog.itcrud.com/api/getUserById?id=123,id参数对应的类型就是query
  3. body:post请求对应的body体,这个就不多说啦(json体写着麻烦,自己意会)
  4. header:请求头数据,在请求的时候,常用@RequestHeader来接收的参数就是请求头内的数据
  5. form:form表单,不解释

从上面的两个例子,加上对@ApiImplicitParam的巨坑参数paramType的理解,就可以知道为什么这个注解不推荐使用啦。但是在请求参数比较多,而且零散的时候可以考虑使用一下,但是需要注意避坑即可。

4. 源代码

码云(gitee):https://gitee.com/itcrud/itcrud-note/tree/master/itcrud-note-1-1


文章作者: 程序猿洞晓
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 程序猿洞晓 !
评论
 上一篇
Spring Data JPA使用必备(三):Spring Data JPA自定义SQL写法 Spring Data JPA使用必备(三):Spring Data JPA自定义SQL写法
Spring Data JPA的前两篇已经写了通过方法名格式自动生成SQL,也简单的提到了@Query注解。但是往往真正的业务逻辑里面,这些是完全不够用的,涉及到一些稍微复杂一点的查询就会有点问题,如根据一组条件中的某几个条件查询(条件不固定),然后再加上分页、排序,这个时候只是使用之前的方法就有点捉襟见肘啦。
下一篇 
参数校验之Hibernate-validator的基本使用 参数校验之Hibernate-validator的基本使用
validation-api在开发过程中是经常使用到的,特别是参数校验,刚开始做开发工作的时候,都是在Service层用if…else…来判断参数的合法性,这个会使代码显得很臃肿,后来接触validation,真的是很好用的。这里分享出来,也是做个笔记,以后用到可以作为参考资料。
2019-05-03
  目录