Swagger 简介
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。Swagger 让部署管理和使用功能强大的API从未如此简单。借助Swagger开源和专业工具集,为用户,团队和企业简化API开发。
swagger是一款可以根据resutful风格生成的生成的接口开发文档,并且支持做测试的一款中间软件。
现在市面上大部分公司都采用前后端分离式的开发规则,前端使用 Vue,Angular,React 等等完成页面开发,后端省掉了视图跳转的过程,直接书写接口返回 json 数据供前端调用即可。
但是书写接口文档是一件费时费力的活,如果后端同开发者能够提供一份清晰明了的接口文档,那么就能极大地提高大家的沟通效率和开发效率。可是编写接口文档历来都是令人头痛的,而且后续接口文档的维护也十分耗费精力。而 Swagger 可以根据程序代码自动生成在线接口文档,Swagger 是接口文档生成工具
最好是有一种方案能够既满足我们输出文档的需要又能随代码的变更自动更新,而Swagger正是那种能帮我们解决接口文档问题的工具。
前后端分离
- 前端 -> 前端控制层、视图层
- 后端 -> 后端控制层、服务层、数据访问层
- 前后端通过API进行交互。前后端相对独立且松耦合
产生的问题
前后端集成,前端或者后端无法做到“及时协商,尽早解决”,最终导致问题集中爆发这是前后端分离项目最大的问题。
解决方案
通过swagger搭配注解使用,可以直接把后端所编写的接口生成在线文档,那么前端可直接根据在线接口文档进行对接。
为什么要使用 Swagger
对于后端开发人员来说
- 不用再手写WiKi接口拼大量的参数,避免手写错误
- 对代码侵入性低,采用全注解的方式,开发简单
- 方法参数名修改、增加、减少参数都可以直接生效,不用手动维护
- 缺点:增加了开发成本,写接口还得再写一套参数配置
对于前端开发来说
- 后端只需要定义好接口,会自动生成文档,接口功能、参数一目了然
- 联调方便,如果出问题,直接测试接口,实时检查参数和返回值,就可以快速定位是前端还是后端的问题
对于测试人员来说
- 对于某些没有前端界面UI的功能,可以用它来测试接口
- 操作简单,不用了解具体代码就可以操作
- 操作简单,不用了解具体代码就可以操作
使用步骤
上面说了一大堆概念,我们知道了解即可。下面我们学习一下SpringBoot中是如何使用Swagger。🥰
创建数据库
CREATE TABLE `book` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '图书名称',
`author` varchar(10) DEFAULT NULL COMMENT '作者',
`price` decimal(10,0) DEFAULT NULL COMMENT '价格',
`publish_date` datetime DEFAULT NULL COMMENT '上架日期',
PRIMARY KEY (`id`)
)
insert into book VALUE (1,'三体','刘慈欣',98,'2018-03-20 08:00:00');
添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--数据驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!--swagger2 依赖-->
<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>
</dependencies>
添加配置类
使用Swagger之前要先进行配置,我们需要编写一个SwaggerConfig的配置类
package com.swagger.swaggerboot.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
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;
/**
* Description:
* Swagger 配置类
* @author ChenJia
* @date 2020/9/10 17:00
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig implements WebMvcConfigurer {
@Value("${swagger.basePackage}")
private String basePackage;// 扫描controler的包名
@Value("${swagger.title}")
private String title;// 在线文档的标题
@Value("${swagger.description}")
private String description;// 在线文档的描述
@Value("${swagger.contact}")
private String contact;// 联系人
@Value("${swagger.version}")
private String version;// 文档版本
@Value("${swagger.url}")
private String url;// URL
@Bean
public Docket createApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()// 函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现
.apis(RequestHandlerSelectors.basePackage(basePackage)).paths(PathSelectors.any()).build();
}
/**
* apiInfo()用来创建该Api的基本信息
* 这些基本信息会展现在文档页面中
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title(title)
.description(description)
.version(version)
.contact(contact)
.licenseUrl(url).build();
}
/**
* swagger-ui.html路径映射,浏览器中使用/api-docs访问
* @param registry
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController("/api", "/swagger-ui.html");// 添加服务api访问文档url
}
}
配置扫包
Swagger生成文档通常是根据Controller去生成的,那如何去配置扫描那些控制层进行生成呢?
@Bean
public Docket createApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()// 通过.select()方法,去配置扫描接口,RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any()).build();
}
配置类配置说明
Swagger的实例Bean是Docket 所以我们通过实例Docket来配置
@Bean
public Docket createApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())//配置Swagger的详细信息,如下apiInfo()
.select()// 函数返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any()).build();
}
/**
* apiInfo()用来创建该Api的基本信息
* 这些基本信息会展现在文档页面中
* 下方动态的从springboot配置文件读取
*/
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title(title)//标题
.description(description)// 描述
.version(version)// 版本
.contact(contact)// 联系人信息
.license(license)
.licenseUrl(url)// 许可连接
.build();
}
配置Swagger开关
Swagger是为了方便,前端和后端开发人员而存在的,如果项目上线后Swagger文档就没有存在的必要了,这时候我们就要配置关闭Swagger,操作如下:
@Bean
public Docket createApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(false)//关闭Swagger
.select()
.apis(RequestHandlerSelectors.basePackage(basePackage)).paths(PathSelectors.any()).build();
}
如何实现动态配置判断是否正式环境进行开启和关闭呢?
动态配置当项目处于test、dev环境时显示swagger,处于prod时不显示
- 在不同配置文件中配置不同的标识,用来区分是否开启,类似数据源配置
- 基于Profile进行配置。我们知道@Profile可以指定组件在哪个环境的情况下才能被注册到容器中。默认不指定,任何环境下都能注册这个组件。
springboot的配置文件
# 配置端口防止冲突
server:
port: 8081
# mysql配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/testdb?useUnicode=true&characterEncoding=utf-8
username: root
password: 521521
# swagger配置
swagger:
basePackage: com.swagger.swaggerboot.controller
title: 书籍管理系统
description: 学习Swagger测试案例。书店平台的REST API,通过JSON访问对象模型数据。
contact: qianfeng
version: v1.0
license: Apach 2.0
url: http://www.1000phone.com
创建实体类
public class Book {
private String id;
private String name;
private String author;
private String price;
private String publish_date;
//get和set省略...
}
Dao
public interface BookMapper {
public List<Book> findAll();
public Book findById(int id);
public Integer saveBook(Book book);
public Integer update(Book book);
public Integer delete(int id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.swagger.swaggerboot.mapper.BookMapper">
<!--public List<Book> findAll();-->
<select id="findAll" resultType="Book">
select * from book
</select>
<!--public Book findById(int id);-->
<select id="findById" resultType="Book">
select * from book
<where>
<if test="id!=null and id !=''">
and id = #{id}
</if>
</where>
</select>
<!--public void saveBook(Book book);-->
<insert id="saveBook">
insert into book values (default ,#{name},#{author},#{price},#{publish_date})
</insert>
<!--public void update(Book book);-->
<update id="update" parameterType="Book">
UPDATE book
<set>
<if test ='null != name'>name = #{name},</if>
<if test ='null != author'>author = #{author},</if>
<if test ='null != price'>price = #{price},</if>
<if test ='null != publishDate'>publish_date = #{publishDate}</if>
</set>
WHERE id = #{id}
</update>
<!--public void delete(int id);-->
<delete id="delete" >
DELETE FROM book WHERE id = #{id}
</delete>
</mapper>
Service
public interface BookService {
public List<Book> findAll();
public Book findById(int id);
public Integer saveBook(Book book);
public Integer update(Book book);
public Integer delete(int id);
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookMapper bookMapper;
@Override
public List<Book> findAll() {
return bookMapper.findAll();
}
@Override
public Book findById(int id) {
return bookMapper.findById(id);
}
@Override
public Integer saveBook(Book book) {
return bookMapper.saveBook(book);
}
@Override
public Integer update(Book book) {
return bookMapper.update(book);
}
@Override
public Integer delete(int id) {
return bookMapper.delete(id);
}
}
Controller
package com.swagger.swaggerboot.controller;
import com.swagger.swaggerboot.pojo.Book;
import com.swagger.swaggerboot.service.BookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Description:
* Swagger 测试Controller
* @author ChenJia
* @date 2020/9/10 17:00 */
@RestController
@RequestMapping("/api")
@Api(description = "图书管理api",value = "管理图书")
public class BookController {
@Autowired
private BookService bookService;
@RequestMapping("/findAll")
@ApiOperation(value = "查询所有图书")
public List<Book> findAll() {
return bookService.findAll();
}
@GetMapping("findById")
@ApiOperation(value = "查询根据ID查询图书")
public Book findById(@ApiParam(required = true,value ="图书编号")Integer id){
return bookService.findById(id);
}
@PostMapping("/saveBook")
@ApiOperation(value = "新增图书")
public Book save(@ApiParam(value = "图书对象") Book book){
bookService.saveBook(book);
System.out.println(book);
return book;
}
@PutMapping("/updateBook")
@ApiOperation(value = "修改图书",response = Book.class)
public Book update(@ApiParam(value = "图书对象") Book book){
bookService.update(book);
return book;
}
@DeleteMapping("/deleteBook")
@ApiOperation(value = "删除图书")
public Map delete(@ApiParam(required = true,value ="图书编号")Integer id){
Map map = new HashMap();
Integer delete = bookService.delete(id);
if (delete>0){
map.put("result","success");
}else{
map.put("result","fail");
}
return map;
}
}
效果
常用注解
- @Api():用在类上,表示标识这个类是swagger的资源,说明该类的主要作用。
- tags:类的名称。可以有多个值,多个值表示多个副本。
- @ApiOperation():写在方法上,给API增加方法说明描述。
- value:接口方法描述
- notes:接口方法提示信息
- response:请求方法相应返回的数据对象描述
- tags可以重新分组
- @ApiModel():是类上注解,主要应用 Model,也就是说这个注解一 般都是写在实体类上。
- value:名称
- description:描述用于类
- @ApiModelProperty():用于方法,字段,表示对model属性的说明或者数据操作更改
- name:重写属性名
- value:描述
- required:是否是必须的
- example:示例内容
- hidden:是否隐藏。
- @ApiIgnore():用于类,方法,方法参数,表示这个方法或者类被忽略
- @ApiParam(): 写在方法参数前面。用于对参数进行描述或说明
- name:参数名称
- value:参数描述
- required:是否是必须
- @ApiImplicitParam() :用来注解来给方法入参增加说明。
- name–参数ming
- value–参数说明
- dataType–数据类型
- paramType–参数类型
- example–举例说明
- @ApiImplicitParams() 用于方法,包含多个 @ApiImplicitParam,如果希望在方法上配置多个参数时,使用。
@ApiImplicitParams(value={
@ApiImplicitParam(name="id",value = "编号",required = true),
@ApiImplicitParam(name="name",value = "图书名称",required = true)
})
public People getPeople2(Long id, String name, String address) {