swagger&knife4j

2023-04-24 17:50:27 浏览数 (3)

Swagger

方案一

一、认识Swagger

1.Swagger简介

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。

2.Swagger主要功能

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

A. 使得前后端分离开发更加方便,有利于团队协作

B. 接口文档在线自动生成,降低后端开发人员编写接口文档的负担

C. 接口功能测试

使用Swagger只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。

3.官方网站

官网:https://swagger.io/

二、使用方式

1.引入Srpingfox依赖

代码语言:javascript复制
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
​
    <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>
​

[注意]

1..使用swagger要求jdk1.8及以上,否则swagger2不能运行

2.springboot集成swagger需要引入两个包即以上两个

2.创建配置类

代码语言:javascript复制
@Configuration
@EnableSwagger2
public class Swagger2Config {
    /**
     * 创建API应用
     * apiInfo() 增加API相关信息
     * 通过select()函数返回一个ApiSelectorBuilder实例,用来控制哪些接口暴露给Swagger来展现,
     * 指定扫描的包路径来定义指定要建立API的目录。
     * @return
     */
    @Bean
    public Docket coreApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(adminApiInfo())
                .groupName("adminApi")
                .select()
                //只显示admin下面的路径
                .paths(Predicates.and(PathSelectors.regex("/admin/.*")))
                .build();
    }
​
    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("尚融宝后台管理系统--api文档")
                .description("尚融宝后台管理系统接口描述")
                .version("1.0")
                .contact(new Contact("李燕茹","http://baidu.com","728831102@qq.com"))
                .build();
    }
}
​

3.API详解

@Api:用在类上,说明该类的作用。 @ApiOperation:注解来给API增加方法说明。 @ApiParam:定义在参数上 @ApiResponses:用于表示一组响应 @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 l code:数字,例如400 l message:信息,例如"请求参数没填好" l response:抛出异常的类

@ApiModel:描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)

l @ApiModelProperty:描述一个model的属性

@ApiImplicitParams: 用在方法上包含一组参数说明。

@ApiImplicitParam:用来注解来给方法入参增加说明

例子:

代码语言:javascript复制
//实体类
//entity的实体类中可以添加一些自定义设置
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="IntegralGrade对象", description="积分等级表")
public class IntegralGrade implements Serializable {
​
    private static final long serialVersionUID = 1L;
​
    @ApiModelProperty(value = "编号")
      @TableId(value = "id", type = IdType.AUTO)
    private Long id;
​
    @ApiModelProperty(value = "积分区间开始")
    private Integer integralStart;
​
    @ApiModelProperty(value = "积分区间结束")
    private Integer integralEnd;
​
    @ApiModelProperty(value = "借款额度")
    private BigDecimal borrowAmount;
​
    @ApiModelProperty(value = "创建时间")
    private LocalDateTime createTime;
​
    @ApiModelProperty(value = "更新时间")
    private LocalDateTime updateTime;
​
    @ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
    @TableField("is_deleted")
    @TableLogic
    private Boolean deleted;
}
​
代码语言:javascript复制
//controler层
@RestController
@RequestMapping("/admin/integralGrade")
@Api(value = "积分等级管理")
public class IntegralGradeController {
​
    @Resource
    private IntegralGradeService integralGradeService;
​
    @GetMapping("/list")
    @ApiOperation("积分等级列表")
    public Result listAll(){
        List<IntegralGrade> list = integralGradeService.list();
        return Result.ok().data("list",list);
    }
​
    @DeleteMapping("/remove/{id}")
    @ApiOperation(value = "根据id删除积分等级",notes = "逻辑删除")
    public Result removeById(
        @ApiParam(value = "数据id",required = true,example = "1")
        @PathVariable Long id){
        boolean result = integralGradeService.removeById(id);
        if (result){
            return Result.ok().message("删除成功");
        }else {
            return Result.error().message("删除失败");
        }
    }
​
    @PostMapping("/save")
    @ApiOperation(value = "新增积分等级")
    public Result save(@ApiParam(value = "积分等级对象",required = true) @RequestBody IntegralGrade integralGrade){
        boolean result = integralGradeService.save(integralGrade);
        if (result){
            return Result.ok().message("新增成功");
        }else {
            return Result.error().message("新增失败");
        }
    }
​
    @PutMapping("/updateById")
    @ApiOperation(value = "根据id修改积分等级")
    public Result updateById(@ApiParam(value = "积分等级对象",required = true) @RequestBody IntegralGrade integralGrade){
        boolean result = integralGradeService.updateById(integralGrade);
        if (result){
            return Result.ok().message("修改成功");
        }else {
            return Result.error().message("修改失败");
        }
    }
​
    @GetMapping("/getById/{id}")
    @ApiOperation(value = "根据id查询积分等级")
    public Result getById(@ApiParam(value = "数据id",required = true,example = "1") @PathVariable Long id){
        IntegralGrade result = integralGradeService.getById(id);
        if (result == null){
            return Result.error().message("查询失败");
        }else {
            return Result.ok().data("integralGrade",result);
        }
    }
}
​

4.访问页面

完成上述代码,启动Spring Boot程序,

访问:http://localhost:8080/swagger-ui.html

5.测试

点击try it out 可以测试接口

方案二

1.导包

引入依赖 需要依赖的工程

代码语言:javascript复制
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
</dependency>

2.配置

代码语言:javascript复制
package com.heima.common.swagger;
​
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.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
​
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {
​
   @Bean
   public Docket buildDocket() {
      return new Docket(DocumentationType.SWAGGER_2)
              .apiInfo(buildApiInfo())
              .select()
              // 要扫描的API(Controller)基础包
              .apis(RequestHandlerSelectors.basePackage("com.heima"))
              .paths(PathSelectors.any())
              .build();
   }
​
   private ApiInfo buildApiInfo() {
      Contact contact = new Contact("总标题","","");
      return new ApiInfoBuilder()
              .title("API文档")
              .description("后台api")
              .contact(contact)
              .version("1.0.0").build();
   }
}

resources/META-INF/Spring.factories

代码语言:javascript复制
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
  com.heima.common.swagger.SwaggerConfiguration

加上注解就能使用了

Swagger常用注解

代码语言:javascript复制
在Java类中添加Swagger的注解即可生成Swagger接口文档,常用Swagger注解如下:
​
@Api:修饰整个类,描述Controller的作用  
​
@ApiOperation:描述一个类的一个方法,或者说一个接口  
​
@ApiParam:单个参数的描述信息  
​
@ApiModel:用对象来接收参数  
​
@ApiModelProperty:用对象接收参数时,描述对象的一个字段  
​
@ApiResponse:HTTP响应其中1个描述  
​
@ApiResponses:HTTP响应整体描述  
​
@ApiIgnore:使用该注解忽略这个API  
​
@ApiError :发生错误返回的信息  
​
@ApiImplicitParam:一个请求参数  
​
@ApiImplicitParams:多个请求参数的描述信息

三、技术概述

参考:

http://blog.didispace.com/springbootswagger2/

Swagger官网 :

http://swagger.io/

Spring Boot & Swagger UI :

http://fruzenshtein.com/spring-boot-swagger-ui/

Github:

https://github.com/swagger-api/swagger-core/wiki/Annotations

knife4j

一、认识knife4j

knife4j是swagger的增强版,更契合微服务架构,ui前身是swagger-bootstrap-ui,api注解的使用方式和swagger一致。

二、使用方式

1.引入依赖

代码语言:javascript复制
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-boot-starter</artifactId>
    <version>3.0.2</version>
</dependency>

2、导入相关配置类

这里我们就不需要再创建一个新的配置类了,我们直接在WebMvcConfig配置类中声明即可。

A. 在该配置类中加上两个注解 @EnableSwagger2 @EnableKnife4j ,开启Swagger和Knife4j的功能。

B. 在配置类中声明一个Docket类型的bean, 通过该bean来指定生成文档的信息。

代码语言:javascript复制
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
	
    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始进行静态资源映射...");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }
	
    /**
     * 扩展mvc框架的消息转换器
     * @param converters
     */
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("扩展消息转换器...");
        //创建消息转换器对象
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        //设置对象转换器,底层使用Jackson将Java对象转为json
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        //将上面的消息转换器对象追加到mvc框架的转换器集合中
        converters.add(0,messageConverter);
    }
	
    @Bean
    public Docket createRestApi() {
        // 文档类型
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller"))
                .paths(PathSelectors.any())
                .build();
    }
	
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("瑞吉外卖")
                .version("1.0")
                .description("瑞吉外卖接口文档")
                .build();
    }
}

注意: Docket声明时,指定的有一个包扫描的路径,该路径指定的是Controller所在包的路径。因为Swagger在生成接口文档时,就是根据这里指定的包路径,自动的扫描该包下的@Controller, @RestController, @RequestMapping等SpringMVC的注解,依据这些注解来生成对应的接口文档。

3.设置静态资源映射

代码语言:javascript复制
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");

4.在LoginCheckFilter中设置不需要处理的请求路径

需要将Swagger及Knife4j相关的静态资源直接放行,无需登录即可访问,否则我们就需要登录之后,才可以访问接口文档的页面。

在原有的不需要处理的请求路径中,再增加如下链接:

代码语言:javascript复制
"/doc.html",
"/webjars/**",
"/swagger-resources",
"/v2/api-docs"

5.查看接口文档

经过上面的集成配置之后,我们的项目集成Swagger及Knife4j就已经完成了,接下来我们可以重新启动项目,访问接口文档,访问链接为: http://localhost:8080/doc.html

我们可以看到,在所有的Controller中提供的所有的业务增删改查的接口,全部都已经自动生成了,我们通过接口文档可以看到请求的url、请求方式、请求参数、请求实例、响应的参数,响应的示例。 并且呢,我们也可以通过这份在线的接口文档,对接口进行测试。

注意: 由于我们服务端的Controller中的业务增删改查的方法,都是必须登录之后才可以访问的,所以,我们在测试时候,也是需要先访问登录接口。登录完成之后,我们可以再访问其他接口进行测试。

我们不仅可以在浏览器浏览生成的接口文档,Knife4j还支持离线文档,对接口文档进行下载,支持下载的格式有:markdown、html、word、openApi。

6.常用注解

注解

位置

说明

@Api

加载Controller类上,表示对类的说明

@ApiModel

类(通常是实体类)

描述实体类的作用

@ApiModelProperty

属性

描述实体类的属性

@ApiOperation

方法

说明方法的用途、作用

@ApiImplicitParams

方法

表示一组参数说明

@ApiImplicitParam

方法

用在@ApiImplicitParams注解中,指定一个请求参数的各个方面的属性

例子:

代码语言:javascript复制
@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;
}
代码语言:javascript复制
@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(); //动态数据
	
	//省略静态方法 ....
}    
代码语言:javascript复制
@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){
        log.info("套餐信息:{}",setmealDto);

        setmealService.saveWithDish(setmealDto);

        return R.success("新增套餐成功");
    }

    /**
     * 套餐分页查询
     * @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);
    }

    /**
     * 删除套餐
     * @param ids
     * @return
     */
    @DeleteMapping
    @CacheEvict(value = "setmealCache",allEntries = true)
    @ApiOperation(value = "套餐删除接口")
    public R<String> delete(@RequestParam List<Long> ids){
        log.info("ids:{}",ids);

        setmealService.removeWithDish(ids);

        return R.success("套餐数据删除成功");
    }

    /**
     * 根据条件查询套餐数据
     * @param setmeal
     * @return
     */
    @GetMapping("/list")
    @Cacheable(value = "setmealCache",key = "#setmeal.categoryId   '_'   #setmeal.status")
    @ApiOperation(value = "套餐条件查询接口")
    public R<List<Setmeal>> list(Setmeal setmeal){
        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());
        queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        List<Setmeal> list = setmealService.list(queryWrapper);

        return R.success(list);
    }
}

7.重启服务测试

我们上述通过Swagger的注解,对实体类及实体类中的属性,以及Controller和Controller的方法进行描述,接下来,我们重新启动服务,然后看一下自动生成的接口文档有何变化。

在接口文档的页面中,我们可以看到接口的中文描述,清晰的看到每一个接口是做什么的,接口方法参数什么含义,参数是否是必填的,响应结果的参数是什么含义等,都可以清楚的描述出来。

总之,我们要想清晰的描述一个接口,就需要借助于Swagger给我们提供的注解。

0 人点赞