官网:https://swagger.io
一、准备工作
1). 导入knife4j的maven坐标
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
2). 设置静态资源映射
由于Swagger生成的在线文档中,涉及到很多静态资源,这些静态资源需要添加静态资源映射,否则接口文档页面无法访问。因此需要在 WebMvcConfig类中的addResourceHandlers方法中增加如下配置。
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
3). 在LoginCheckFilter中设置不需要处理的请求路径
需要将Swagger及Knife4j相关的静态资源直接放行,无需登录即可访问,否则我们就需要登录之后,才可以访问接口文档的页面。
在原有的不需要处理的请求路径中,再增加如下链接:
"/doc.html",
"/webjars/**",
"/swagger-resources",
"/v2/api-docs"
4).开启Swagger和Knife4j的功能。
这里我们就不需要再创建一个新的配置类了,我们直接在WebMvcConfig配置类中声明即可。
在该配置类中加上两个注解 @EnableSwagger2 @EnableKnife4j ,开启Swagger和Knife4j的功能。
@Slf4j
@Configuration
@EnableSwagger2 //开启Swagger功能
@EnableKnife4j //开启Knife4j功能
public class WebMvcConfig extends WebMvcConfigurationSupport {
}
5). 查看接口文档
二、配置Swagger文档信息
1). Docket实例来配置Swaggger
Swagger实例Bean是Docket,所以通过配置Docket实例来配置Swaggger。
在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的信息。
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
//其他代码略
@Bean
public Docket createRestApi() {
// 文档类型
return new Docket(DocumentationType.SWAGGER_2);
}
}
2). 查看接口文档
3).通过apiInfo()属性配置文档信息
@Bean
public Docket createRestApi() {
//配置文档信息
ApiInfo apiInfo = new ApiInfoBuilder()
.title("瑞吉外卖")
.version("1.0")
.description("瑞吉外卖接口文档")
.build();
// 文档类型 //Docket 实例关联上 apiInfo()
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo);
}
三、配置Swagger配置扫描接口
1). 通过select()方法,配置扫描接口方式
@Bean
public Docket createRestApi() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("瑞吉外卖")
.version("1.0")
.description("瑞吉外卖接口文档")
.build();
// 文档类型
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo)
// 通过.select()方法,去配置扫描接口
.select()
//RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.web"))
.build();
}
2). select()方法,其他方式扫描接口
any() // 扫描所有,项目中的所有接口都会被扫描到
none() // 不扫描接口
// 通过方法上的注解扫描,如:withMethodAnnotation(PostMapping.class)只扫描post请求
withMethodAnnotation(final Class<? extends Annotation> annotation)
// 通过类上的注解扫描,如:withClassAnnotation(Controller.class)只扫描有controller注解的类中的接口
withClassAnnotation(final Class<? extends Annotation> annotation)
// 根据包路径扫描接口
basePackage(final String basePackage)
3). paths()方法,配置接口扫描过滤
any() // 任何请求都扫描 none() // 任何请求都不扫描
regex(final String pathRegex) // 通过正则表达式控制
ant(final String antPattern) // 通过ant()控制
@Bean
public Docket createRestApi() {
ApiInfo apiInfo = new ApiInfoBuilder()
.title("瑞吉外卖")
.version("1.0")
.description("瑞吉外卖接口文档")
.build();
// 文档类型
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo)
// 通过.select()方法,去配置扫描接口
.select()
//RequestHandlerSelectors配置如何扫描接口
.apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.web"))
//配置接口扫描过滤,只扫描请求以/dish开头的接口
.paths(PathSelectors.ant("/dish/**"))
.build();
}
三、常用注解
直接访问Knife4j的接口文档页面,可以查看到所有的接口文档信息,但是我们发现,这些接口文档分类及接口描述都是Controller的类名(驼峰命名转换而来)及方法名,而且在接口文档中,所有的请求参数,响应数据,都没有中文的描述,并不知道里面参数的含义,接口文档的可读性很差。
注解介绍
为了解决上述的问题,Swagger提供了很多的注解,通过这些注解,我们可以更好更清晰的描述我们的接口,包含接口的请求参数、响应数据、数据模型等。核心的注解,主要包含以下几个:
注解 | 位置 | 说明 |
---|---|---|
@Api | 类 | 加载Controller类上,表示对类的说明 |
@ApiModel | 类(通常是实体类) | 描述实体类的作用 |
@ApiModelProperty | 属性 | 描述实体类的属性 |
@ApiOperation | 方法 | 说明方法的用途、作用 |
@ApiImplicitParams | 方法 | 表示一组参数说明 |
@ApiImplicitParam | 方法 | 用在@ApiImplicitParams注解中,指定一个请求参数的各个方面的属性 |
注解测试
实体类
可以通过 @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 = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
}
响应实体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(); //动态数据
//省略静态方法 ....
}
Controller类及其中的方法
描述Controller、方法及其方法参数,可以通过注解: @Api, @APIOperation, @ApiImplicitParams, @ApiImplicitParam
@RestController
@RequestMapping("/setmeal")
@Slf4j
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private CategoryService categoryService;
@Autowired
private SetmealDishService setmealDishService;
/**
* 套餐分页查询
* @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模糊查询
queryWrapper.like(name != null,Setmeal::getName,name);
//添加排序条件,根据更新时间降序排列
queryWrapper.orderByDesc(Setmeal::getUpdateTime);
setmealService.page(pageInfo,queryWrapper);
//对象拷贝
BeanUtils.copyProperties(pageInfo,dtoPage,"records");
List<Setmeal> records = pageInfo.getRecords();
List<SetmealDto> list = records.stream().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
//对象拷贝
BeanUtils.copyProperties(item,setmealDto);
//分类id
Long categoryId = item.getCategoryId();
//根据分类id查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
//分类名称
String categoryName = category.getName();
setmealDto.setCategoryName(categoryName);
}
return setmealDto;
}).collect(Collectors.toList());
dtoPage.setRecords(list);
return R.success(dtoPage);
}
}
重启服务测试:展示修改了套餐模块分页查询的效果