knife4j 简单使用

时间:2025-03-19 10:52:50

为什么要使用Swagger?

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。功能主要包含以下几点:

  1. 使得前后端分离开发更加方便,有利于团队协作
  2. 接口文档在线自动生成,降低后端开发人员编写接口文档的负担
  3. 接口功能测试

使用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);
    

}