Swagger2在项目上的应用,便捷的输出接口文档.

在解决高并发的学习中,接触到swagger2.是一个接口文档的解决方案.开始自己学习在已有的项目上进行部署.将学习到的与遇到的问题进行整理,希望对大家以及以后的工作有所帮助.

使用swagger2需要引入swagger2的pom坐标,特别注明,两个pom坐标的版本需要相同.

<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>${swagger2.version}</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>${swagger2.version}</version>
</dependency>

此处的swagger2.version可以直接RELEASE的版本,将自动使用最新版本.

在Spring boot上集成swagger2需要使用Configuration注解进行注入,此处给出一个范例.

package com.mingcloud.az.common;


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.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author Relic
 * @desc swagger2配置类
 * @date 2019-02-05 15:59
 */
@EnableSwagger2
@Configuration
public class Swagger2Configuration {


    @Bean
    public Docket controllerApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(new ApiInfoBuilder().title("xxxxx接口文档").description("前后端分离使用过的接口文档")
                        .contact(new Contact("Relic", null, null)).version("版本号:1.0").build())
                .select().apis(RequestHandlerSelectors.basePackage("com.mingcloud.az.controller"))
                .paths(PathSelectors.any()).build();
    }


}

swagger通过注解表明该接口会生成文档,包括接口名、请求方法、参数、返回信息的等等。

  • @Api:修饰整个类,描述Controller的作用
  • @ApiOperation:描述一个类的一个方法,或者说一个接口
  • @ApiParam:单个参数描述
  • @ApiModel:用对象来接收参数(在实体类上进行注解)
  • @ApiProperty:用对象接收参数时,描述对象的一个字段(在实体类的属性上进行注解)
  • @ApiResponse:HTTP响应其中1个描述
  • @ApiResponses:HTTP响应整体描述
  • @ApiIgnore:使用该注解忽略这个API
  • @ApiError :发生错误返回的信息
  • @ApiImplicitParam:一个请求参数
  • @ApiImplicitParams:多个请求参数
@Api:用在请求的类上,表示对类的说明
    tags="说明该类的作用,可以在UI界面上看到的注解"
    value="该参数没什么意义,在UI界面上也看到,所以不需要配置"


@ApiOperation:用在请求的方法上,说明方法的用途、作用
    value="说明方法的用途、作用"
    notes="方法的备注说明"


@ApiImplicitParams:用在请求的方法上,表示一组参数说明
    @ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
        name:参数名
        value:参数的汉字说明、解释
        required:参数是否必须传
        paramType:参数放在哪个地方
            · header --> 请求参数的获取:@RequestHeader
            · query --> 请求参数的获取:@RequestParam
            · path(用于restful接口)--> 请求参数的获取:@PathVariable
            · body(不常用)
            · form(不常用)    
        dataType:参数类型,默认String,其它值dataType="Integer"       
        defaultValue:参数的默认值


@ApiResponses:用在请求的方法上,表示一组响应
    @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
        code:数字,例如400
        message:信息,例如"请求参数没填好"
        response:抛出异常的类


@ApiModel:用于响应类上,表示一个返回响应数据的信息
            (这种一般用在post创建的时候,使用@RequestBody这样的场景,
            请求参数无法使用@ApiImplicitParam注解进行描述的时候)
    @ApiModelProperty:用在属性上,描述响应

此处需要引入一个Restful风格接口的概念.

URL定位资源,用HTTP动词(GET,POST,DELETE,DETC)描述操作。

识别(identify)、 表示(represent) 、交互(interact with)。

  • 看Url就知道要什么
  • 看http method就知道干什么
  • 看http status code就知道结果如何

 

1. REST描述的是在网络中client和server的一种交互形式;REST本身不实用,实用的是如何设计 RESTful API(REST风格的网络接口);

 

2. Server提供的RESTful API中,URL中只使用名词来指定资源,原则上不使用动词。“资源”是REST架构或者说整个网络处理的核心。比如:

http://api.qc.com/v1/newsfeed: 获取某人的新闻推送;

http://api.qc.com/v1/friends: 获取某人的好友列表;

http://api.qc.com/v1/profile: 获取某人的详细信息;

 

3. 用HTTP协议里的动词来实现资源的添加,修改,删除等操作。即通过HTTP动词来实现资源的状态扭转:

GET 用来获取资源,

POST 用来新建资源(也可以用于更新资源),

PUT 用来更新资源,

DELETE 用来删除资源。比如:

DELETE http://api.qc.com/v1/friends: 删除某人的好友 (在http parameter指定好友id)

POST http://api.qc.com/v1/friends: 添加好友

UPDATE http://api.qc.com/v1/profile: 更新个人资料

 

4. Server和Client之间传递某资源的一个表现形式,比如用JSON,XML传输文本,或者用JPG,WebP传输图片等。当然还可以压缩HTTP传输时的数据(on-wire data compression)。

 

5. 用 HTTP Status Code传递Server的状态信息。比如最常用的 200 表示成功,500 表示Server内部错误等。

 

1、REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。

比如:左边是错误的设计,而右边是正确的

GET /rest/api/getDogs --> GET /rest/api/dogs 获取所有小狗狗 
GET /rest/api/addDogs --> POST /rest/api/dogs 添加一个小狗狗 
GET /rest/api/editDogs/:dog_id --> PUT /rest/api/dogs/:dog_id 修改一个小狗狗 
GET /rest/api/deleteDogs/:dog_id --> DELETE /rest/api/dogs/:dog_id 删除一个小狗狗

 

2、REST很好地利用了HTTP本身就有的一些特征,如HTTP动词、HTTP状态码、HTTP报头等等。

  • HTTP动词
GET     获取一个资源 
POST    添加一个资源 
PUT     修改一个资源 
DELETE  删除一个资源
  • HTTP状态码
200 OK 
400 Bad Request 
500 Internal Server Error
  • HTTP报头
Authorization 认证报头 
Cache-Control 缓存报头 
Cnotent-Type  消息体类型报头 
......

在控制层上加上注解.

package com.mingcloud.az.controller;


import com.mingcloud.az.common.ConfigurationTypeEnum;
import com.mingcloud.az.common.Result;
import com.mingcloud.az.entity.ConfigurationEntity;
import com.mingcloud.az.service.ConfigurationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author xyc
 */
@Api("相关属性(时间.等)的配置")
@RestController
@RequestMapping("/configuration")
public class ConfigurationController {

    @Resource
    private ConfigurationService configurationService;


    @ApiOperation("根据条件获取配置")
    @GetMapping("/doGetConfig.do")
    public Result getConfig(ConfigurationEntity entity) {
        Result result;
        try {
            entity.setTypeId(ConfigurationTypeEnum.getConfigurationByType(entity.getTypeStr()));
            result = configurationService.getConfig(entity);
        } catch (Exception e) {
            e.printStackTrace();
            result = new Result(false, e.getMessage());
        }
        return result;
    }

    /**
     * 更新配置参数接口,如需更新的typeId数据库没有,则新建,如果存在就更新
     *
     * @param entity 配置实体类
     * @return com.mingcloud.az.common.Result
     * @title updateConfig
     * @date 2019/1/17 15:48
     */
    @PutMapping("/doUpdateConfig.do")
    public Result updateConfig(ConfigurationEntity entity) {
        Result result;
        try {
            entity.setTypeId(ConfigurationTypeEnum.getConfigurationByType(entity.getTypeStr()));
            result = configurationService.updateConfig(entity);
        } catch (Exception e) {
            e.printStackTrace();
            result = new Result(false, e.getMessage());
        }
        return result;
    }

}

实体类上添加上注解

package com.mingcloud.az.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;

import java.util.Date;

/**
 * @author Relic
 */
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("配置实体类")
public class ConfigurationEntity extends BaseEntity {

    @ApiModelProperty(hidden = true)
    private Long id;

    @ApiModelProperty(value = "类型id")
    private Integer typeId;

    @ApiModelProperty("备注")
    private String memo;

    @ApiModelProperty(value = "额外id1")
    private Integer extraId1;

    @ApiModelProperty(value = "额外id2")
    private Integer extraId2;

    @ApiModelProperty(value = "额外id3")
    private Integer extraId3;

    @ApiModelProperty(value = "天数")
    private Integer extraInt1;

    @ApiModelProperty(value = "额外整数2")
    private Integer extraInt2;

    @ApiModelProperty(value = "额外整数3")
    private Integer extraInt3;

    @ApiModelProperty("额外字符串1")
    private String extraVarChar1;

    @ApiModelProperty("额外字符串2")
    private String extraVarChar2;

    @ApiModelProperty("额外字符串3")
    private String extraVarChar3;

    @ApiModelProperty("类型名称")
    private String typeStr;

    @ApiModelProperty("修改日期")
    private Date modificationDate;

    @ApiModelProperty(value = "修改用户id")
    private Integer modificationUserId;

}

最后,启动项目,访问 http://ip:port/项目名/swagger-ui.html页面

swagger2一个很实用的功能就就是在测试接口功能上.点击接口上的try it out.然后填写参数,点击execute.

可以直接在编写前端代码之前直接进行测试.

下面说几个遇到的坑- -.

1.访问swagger2-ui.html的时候出现弹框.

一开始以为是context-path的问题.去掉context-path的确能够访问,但是不是长久之计.

后来发现只要清除缓存之后,即可解决这个问题.

2.访问swagger2-ui.html界面时,后台一直报错:Illegal DefaultValue null for parameter type integer

原因是由于swagger2的bug,在没有给定integer与long类型的变量默认值的情况下,默认是""值,所以强转失败.对程序本身没有什么影响,但是毕竟本人是个强迫症患者= =.所以在调高日志打印级别为error.

logging.level.io.swagger.models.parameters.AbstractSerializableParameter: error

顺利解决.