在项目中Swagger的使用

  之前没有进行过前后端分离的开发,这次有一个偶然的机会做的项目采用这种模式,在沟通过程中,了解Swagger这个工具,可以很方便的进行前后端的对接,来学习一下

1 Swagger是什么

  Swagger是一款RESTFUL接口的文档在线自动生成+功能测试功能软件。Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化RestFul风格的Web服务。目标是是客户端和文件系统作为服务器以同样的速度来更新文件的方法,参数和模型紧密集成到服务器。

2 为什么要使用Swagger

  1. 对于后端人员来说
    • 不用再手写wiki接口拼大量的参数,避免手写错误
    • 对代码侵入性低,采用全注解的方式,开发简单
    • 方法参数名修改、增加、减少参数都可以直接生效,不用手动维护
    • 缺点,增加了开发成本,写接口还得在写一套配置参数
  2. 对于前端人员来说
    • 后端只需要定义好接口,会自动生成文档,接口功能、参数一目了然
    • 联调方便,如果出问题,直接测试接口,实时检查参数和返回值,就可以快速定位前端还是后端问题
  3. 对于测试
    • 对于某些没有前端界面UI的功能,可以用它来测试接口
    • 操作简单

3 项目集成Swagger

3.1 在项目中使用

  1. 导入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 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>
  2. 添加配置类,然后再开启Swagger2服务

    1
    2
    3
    4
    @Configuration
    @EnableSwagger2 //开启swagger
    public class SwaggerConfig {
    }
  3. 测试运行 http://localhost:8080/swagger-ui.html#/![image-20220505195716306](https://qnjy.coding.net/p/githubimg/d/githubimg/git/raw/master/img/20220505-image-20220505195716306.png)

3.2 配置扫描接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

@Configuration
@EnableSwagger2 //开启swagger
public class SwaggerConfig {

//配置Swagger的docket的bean实例
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//RequestHandlerSelectors,配置要扫描的接口的方式
//bashPackage,指定要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.hdnj.swagger.controller"))
//只扫描指定包下
.paths(PathSelectors.ant("/swagger/**"))
.build();
}

//配置Swagger信息apiInfo
private ApiInfo apiInfo() {

Contact contact = new Contact("qnjy", "", "877900490@qq.com");

return new ApiInfo("你好的Api Documentation",
"也能远航",
"1.0",
"qnjy.github.io",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());

}
}

我只希望Swagger只在生产环境中使用,在发布的时候不使用?

  • 判断是不是生产环境 flag = false

  • 注入enable(false)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
      
    //配置Swagger的docket的bean实例
    @Bean
    public Docket docket(Environment environment) {

    //设置要显示的Swagger环境
    Profiles profiles = Profiles.of("dev");
    //获取项目的开发环境:
    //通过environment.acceptsProfiles来获取环境boolean值
    boolean flag = environment.acceptsProfiles(profiles);

    return new Docket(DocumentationType.SWAGGER_2)
    .apiInfo(apiInfo())
    .enable(flag)
    .select()
    .apis(RequestHandlerSelectors.basePackage("com.hdnj.swagger.controller"))
    .build();
    }

    配置API文档的分组

    1
    .groupName("张三")
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Bean
    public Docket docket1() {
    return new Docket(DocumentationType.SWAGGER_2.groupName("张三");
    }
    @Bean
    public Docket docket2() {
    return new Docket(DocumentationType.SWAGGER_2.groupName("李四");
    }
    @Bean
    public Docket docket3() {
    return new Docket(DocumentationType.SWAGGER_2.groupName("王老五");
    }

    实体类的配置:在pojo实体类中

    1
    2
    3
    4
    5
    6
    7
    8
    @ApiModel("用户实体类")
    public class User{
    @ApiModelProperty("用户名")
    public String username;

    @ApiModelProperty("密码")
    public String password:
    }

    控制层

    1
    2
    3
    4
    5
    6
    //Operation接口,不是放在类上的,是方法
    @Apioperation("Hello控制类")
    @GetMapping(value ="/hello2")
    public string hello2(@ApiParam("用户名")String username){
    return "hello"+username;
    }

4 注解使用

4.1 请求类的说明

1
2
3
@Api:放在 请求的类上,与 @Controller 并列,说明类的作用,如用户模块,订单类等。
tags="说明该类的作用"
value="该参数没什么意义,所以不需要配置"

实例:

1
2
3
4
5
@Api(tags="订单模块")
@Controller
public class OrderController {

}
属性名称 作用
value url的路径值
tags 如果设置这个值、value的值会被覆盖
description 对api资源的描述
basePath 基本路径
position 如果配置多个Api 想改变显示的顺序位置
produces 如, “application/json, application/xml”
consumes 如, “application/json, application/xml”
protocols 协议类型,如: http, https, ws, wss.
authorizations 高级特性认证时配置
hidden 配置为true ,将在文档中隐藏

4.2 方法的说明

1
2
3
@ApiOperation"用在请求的方法上,说明方法的作用"
value="说明方法的作用"
notes="方法的备注说明"

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Api(tags="用户模块")
@Controller
public class UserController {

@ApiOperation(value="用户登录",notes="随边说点啥")
@ApiImplicitParams({
@ApiImplicitParam(name="mobile",value="手机号",required=true,paramType="form"),
@ApiImplicitParam(name="password",value="密码",required=true,paramType="form"),
@ApiImplicitParam(name="age",value="年龄",required=true,paramType="form",dataType="Integer")
})
@PostMapping("/login")
public JsonResult login(@RequestParam String mobile, @RequestParam String password,
@RequestParam Integer age){
//...
return JsonResult.ok(map);
}

}

4.3 返回状态码说明

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Api(tags="用户模块")
@Controller
public class UserController {
@ApiOperation("获取用户信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType="query", name="userId", dataType="String", required=true, value="用户Id")
})
@ApiResponses({
@ApiResponse(code = 200, message = "请求成功"),
@ApiResponse(code = 400, message = "请求参数没填好"),
@ApiResponse(code = 404, message = "请求路径没有或页面跳转路径不对")
})
@ResponseBody
@RequestMapping("/list")
public JsonResult list(@RequestParam String userId) {
...
return JsonResult.ok().put("page", pageUtil);
}
}

4.4 实体类说明

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ApiModel(description = "用户登录")
public class UserLoginVO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "用户名",required=true)
private String username;
@ApiModelProperty(value = "密码",required=true)
private String password;
// getter/setter省略
}
@Api(tags="用户模块")
@Controller
public class UserController {
@ApiOperation(value = "用户登录", notes = "")
@PostMapping(value = "/login")
public R login(@RequestBody UserLoginVO userLoginVO) {
User user=userSerivce.login(userLoginVO);
return R.okData(user);
}
}

5 Gateway整合Swagger

  1. 在gateway微服务里面的config包下新建SwaggerProvider

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package com.hdnj.gateway.config;

    import lombok.AllArgsConstructor;
    import org.springframework.cloud.gateway.support.NameUtils;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    import springfox.documentation.swagger.web.SwaggerResource;
    import springfox.documentation.swagger.web.SwaggerResourcesProvider;
    import org.springframework.cloud.gateway.config.GatewayProperties;
    import org.springframework.cloud.gateway.route.RouteLocator;

    import java.util.ArrayList;
    import java.util.List;


    @Component
    @Primary
    @AllArgsConstructor
    public class SwaggerProvider implements SwaggerResourcesProvider {
    public static final String API_URI = "/v2/api-docs";
    private final RouteLocator routeLocator;
    private final GatewayProperties gatewayProperties;


    @Override
    public List<SwaggerResource> get() {
    List<SwaggerResource> resources = new ArrayList<>();
    List<String> routes = new ArrayList<>();
    /**
    * 获取Gateway配置文件的route参数
    */
    routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
    /**
    * 结合配置的route-path和route过滤,只获取有效的route节点
    */
    gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
    .forEach(routeDefinition -> routeDefinition.getPredicates().stream()
    .filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()))
    .forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
    predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
    .replace("/**", API_URI)))));

    return resources;
    }

    private SwaggerResource swaggerResource(String name, String location) {
    SwaggerResource swaggerResource = new SwaggerResource();
    swaggerResource.setName(name);
    swaggerResource.setLocation(location);
    swaggerResource.setSwaggerVersion("2.0");
    return swaggerResource;
    }
    }
  2. 新建包handler,在此下新建类SwaggerHandler

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    package com.hdnj.gateway.handler;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import reactor.core.publisher.Mono;
    import springfox.documentation.swagger.web.*;

    import java.util.Optional;

    @RestController
    @RequestMapping("/swagger-resources")
    public class SwaggerHandler {
    @Autowired(required = false)
    private SecurityConfiguration securityConfiguration;
    @Autowired(required = false)
    private UiConfiguration uiConfiguration;
    private final SwaggerResourcesProvider swaggerResources;

    @Autowired
    public SwaggerHandler(SwaggerResourcesProvider swaggerResources) {
    this.swaggerResources = swaggerResources;
    }


    @GetMapping("/configuration/security")
    public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
    return Mono.just(new ResponseEntity<>(
    Optional.ofNullable(securityConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("/configuration/ui")
    public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
    return Mono.just(new ResponseEntity<>(
    Optional.ofNullable(uiConfiguration).orElse(UiConfigurationBuilder.builder().build()), HttpStatus.OK));
    }

    @GetMapping("")
    public Mono<ResponseEntity> swaggerResources() {
    return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
    }
    }
  3. 在网关微服务的applicaition.yml下添加filter

    image-20220511171417396

    效果:

    image-20220511171613716