为什么要使用Swagger?
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。功能主要包含以下几点:
- 使得前后端分离开发更加方便,有利于团队协作
- 接口文档在线自动生成,降低后端开发人员编写接口文档的负担
- 接口功能测试
使用Swagger只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。
如何使用Swagger?编写繁琐吗?有什么简化文档的框架吗?
直接使用Swagger, 需要按照Swagger的规范定义接口, 实际上就是编写Json文件,编写起来比较繁琐、并不方便。
而在项目中使用,我们一般会选择一些现成的框架来简化文档的编写,而这些框架是基于Swagger的,如knife4j
knife4j 是为Java MVC框架集成Swager生成Api文档的增强解决方案。而我们要使用kinfe4j,需要在中引入依赖即可
Knife4j 使用方式
1). 导入knife4j的maven坐标
<dependency>
<groupId></groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2). 导入knife4j相关配置类
直接在WebMvcConfig配置类中声明即可。
- A. 配置类中加上注解
@EnableSwagger2
@EnableKnife4j
开启Swagger和Knife4j的功能。 - B. 在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的信息。
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
// 在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的信息
@Bean
public Docket createRestApi() {
// 文档类型
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//更改为自己项目的路径:
.apis((""))
.paths(())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("伊武标题")
.version("1.0")
.description("四月是谎言")
.build();
}
}
注意:
Docket声明时,指定的有一个包扫描的路径,该路径指定的是Controller所在包的路径。因为Swagger在生成接口文档时,就是根据这里指定的包路径,自动的扫描该包下的@Controller, @RestController, @RequestMapping等SpringMVC的注解,依据这些注解来生成对应的接口文档
3). 设置静态资源映射
由于Swagger生成的在线文档中,涉及到很多静态资源,这些静态资源需要添加静态资源映射,否则接口文档页面无法访问。因此需要在 WebMvcConfig类中的addResourceHandlers方法中增加如下配置。
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
("开始进行静态资源映射。。。");
("")
.addResourceLocations("classpath:/META-INF/resources/");
("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
4). 在LoginCheckFilter中设置不需要处理的请求路径
需要将Swagger及Knife4j相关的静态资源直接放行,无需登录即可访问,否则我们就需要登录之后,才可以访问接口文档的页面。
在原有的不需要处理的请求路径中,再增加如下链接:
"/",
"/webjars/**",
"/swagger-resources",
"/v2/api-docs"
查看接口文档
经过上面的集成配置之后,我们的项目集成Swagger及Knife4j就已经完成了,接下来我们可以重新启动项目,访问接口文档,访问链接为: http://localhost:8080/
注意: 由于我们服务端的Controller中的业务增删改查的方法,都是必须登录之后才可以访问的,所以,我们在测试时候,也是需要先访问登录接口。登录完成之后,我们可以再访问其他接口进行测试。
问题说明
在上面我们直接访问Knife4j的接口文档页面,可以查看到所有的接口文档信息,但是我们发现,这些接口文档分类及接口描述都是Controller的类名(驼峰命名转换而来)及方法名,而且在接口文档中,所有的请求参数,响应数据,都没有中文的描述,并不知道里面参数的含义,接口文档的可读性很差。
注解介绍
为了解决上述的问题,Swagger提供了很多的注解,通过这些注解,我们可以更好更清晰的描述我们的接口,包含接口的请求参数、响应数据、数据模型等。核心的注解,主要包含以下几个:
注解 |
位置 |
说明 |
@Api |
类 |
加载Controller类上,表示对类的说明 |
@ApiModel |
类(通常是实体类) |
描述实体类的作用 |
@ApiModelProperty |
属性 |
描述实体类的属性 |
@ApiOperation |
方法 |
说明方法的用途、作用 |
@ApiImplicitParams |
方法 |
表示一组参数说明 |
@ApiImplicitParam |
方法 |
用在@ApiImplicitParams注解中,指定一个请求参数的各个方面的属性 |
注解测试
1). 实体类
可以通过 @ApiModel 标题 , @ApiModelProperty 来描述实体类及属性
@Data
@ApiModel("套餐")
public class Setmeal implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键")
private Long id;
//分类id
@ApiModelProperty("分类id")
private Long categoryId;
//套餐名称
@ApiModelProperty("套餐名称")
private String name;
//套餐价格
@ApiModelProperty("套餐价格")
private BigDecimal price;
//状态 0:停用 1:启用
@ApiModelProperty("状态")
private Integer status;
//编码
@ApiModelProperty("套餐编号")
private String code;
//描述信息
@ApiModelProperty("描述信息")
private String description;
//图片
@ApiModelProperty("图片")
private String image;
@TableField(fill = )
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = )
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
@ApiModelProperty 详细参数
@ApiModelProperty(value = "主键",name = "id", allowableValues = "32", access = "1", notes = "用户的id", dataType = "int", required = false, position = 1, hidden = true, example = "1", readOnly = false, reference = "id", allowEmptyValue = false) @TableId(value = "id",type = ) private int id;
2). 响应实体R
@Data
@ApiModel("返回结果")
public class R<T> implements Serializable{
@ApiModelProperty("编码")
private Integer code; //编码:1成功,0和其它数字为失败
@ApiModelProperty("错误信息")
private String msg; //错误信息
@ApiModelProperty("数据")
private T data; //数据
@ApiModelProperty("动态数据")
private Map map = new HashMap(); //动态数据
//省略静态方法 ....
}
3). Controller类及其中的方法
述Controller、方法及其方法参数,可以通过注解:
@Api (tags="swagger一级标题")
@APIOperation(value=“二级标题”)
@ApiImplicitParams({@ApiImplicitParam (name=“page” , vlaue =“1”, required = " 必须的?")})
@ApiImplicitParam
@RestController
@RequestMapping("/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private CategoryService categoryService;
@Autowired
private SetmealDishService setmealDishService;
/**
* 新增套餐
* @param setmealDto
* @return
*/
@PostMapping
@CacheEvict(value = "setmealCache",allEntries = true)
@ApiOperation(value = "新增套餐接口")
public R<String> save(@RequestBody SetmealDto setmealDto){
("套餐信息:{}",setmealDto);
(setmealDto);
return ("新增套餐成功");
}
/**
* 套餐分页查询
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
@ApiOperation(value = "套餐分页查询接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "page",value = "页码",required = true),
@ApiImplicitParam(name = "pageSize",value = "每页记录数",required = true),
@ApiImplicitParam(name = "name",value = "套餐名称",required = false)
})
public R<Page> page(int page,int pageSize,String name){
//分页构造器对象
Page<Setmeal> pageInfo = new Page<>(page,pageSize);
Page<SetmealDto> dtoPage = new Page<>();
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
//添加查询条件,根据name进行like模糊查询
(name != null,Setmeal::getName,name);
//添加排序条件,根据更新时间降序排列
(Setmeal::getUpdateTime);
(pageInfo,queryWrapper);
//对象拷贝
(pageInfo,dtoPage,"records");
List<Setmeal> records = ();
List<SetmealDto> list = ().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
//对象拷贝
(item,setmealDto);
//分类id
Long categoryId = ();
//根据分类id查询分类对象
Category category = (categoryId);
if(category != null){
//分类名称
String categoryName = ();
(categoryName);
}
return setmealDto;
}).collect(());
(list);
return (dtoPage);
}