# 使用Swagger和SpringFox文档化Spring Boot REST API
> 如果您是第一次了解Swagger,建议您先前往https://swagger.io/官网学习,推荐学习文档《Swagger从入门到实战》,而本文档并不适用于入门。
>
> 如果您仅仅是想了解如何Spring系列项目生成API文档,可先前往[SpringFox](https://springfox.github.io/springfox/)学习。
## Swagger介绍
Swagger 是一个规范且完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
Swagger 的目标是对 REST API 定义一个标准且和语言无关的接口,可以让人和计算机拥有无须访问源码、文档或网络流量监测就可以发现和理解服务的能力。当通过 Swagger 进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger 消除了调用服务时可能会有的猜测。
做后端开发 ,自然离不开有接口文档。接口文档不仅仅方便后端人员之间查看,也方便前端人员查看,也提供给第三方调用接口时查看。但是,编写接口文档太费时间,如果文档格式没有统一,那么每个人写得接口文档可能各不相同。这样就十分混乱,严重影响团队开发进程。因此,有必要寻找到一个方便统一接口文档的工具,swagger就是为此诞生。
如果我们的SpringBoot项目集成了`springfox-swagger-ui`,在**接口**、**入参**、**出参**、**实体类**添加`Swagger注解`,那么,就可以通过访问`http://yourIp:yourport/swgger-ui.html`来访问在线接口,在此页面可以直接进行接口测试。For Example,`http://localhost:8080/swagger-ui.html`
## SpringFox介绍
![image-20220408121605373](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408121605373.png)
自动JSON API文档,用于使用Spring构建的API。
![image-20220408123514816](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408123514816.png)
## 问题:
**如何使用Swagger和SpringFox文档化Spring Boot REST API?**
REST API非常重要。它是一个公共接口,其他模块、应用程序或开发人员需要使用它。拥有适当文档的界面以避免混淆并使其始终保持最新是至关重要的。
最受欢迎的API文档规范之一是OpenApi,以前称为Swagger。它允许您使用JSON或YAML元数据描述API的属性。它还提供了一个Web UI,它可以将元数据转换为一个很好的HTML文档。此外,通过该UI,您不仅可以浏览有关API端点的信息,还可以将UI用作REST客户端 - 您可以调用任何端点,指定要发送的数据并检查响应。它非常方便。
手动编写此类文档并在代码更改时保持更新是不现实的。这就是SpringFox发挥作用的地方。它是Spring Framework的Swagger集成。它可以自动检查您的类,检测控制器,它们的方法,它们使用的模型类以及它们映射到的URL。没有任何手写文档,只需检查应用程序中的类,它就可以生成大量有关API的信息。多么酷啊?最重要的是,每当您进行更改时,它们都会反映在文档中。如下所示。
![image-20220407164517805](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220407164517805.png)
## Swagger的优点与缺点
在此,我们先提出Swagger的优点与缺点,在后续会介绍缘由。
🔶优点:
- `自动生成文档`。只需要在接口中使用注解进行标注,就能生成对应的接口文档。
- `自动更新文档`。由于Swagger是动态生成的,修改接口,文档也会自动对应修改(如果你也更新了注解的话)。这样就不会发生我修改了接口,却忘记更新接口文档的情况。
- `支持在线调试`。swagger提供了在线调用接口的功能。
🔶缺点:
- `不能创建测试用例`。Swagger暂时不能帮助我们处理完所有的事情。他只能提供一个简单的在线调试,如果你想存储你的测试用例,可以使用Postman或者YAPI这样支持创建测试用户的功能。
- `要遵循一些规范`,它不是任意规范的。比如说,你可能会返回一个json数据,而这个数据可能是一个Map格式的,那么我们此时不能标注这个Map格式的返回数据的每个字段的说明,而如果它是一个实体类的话,我们可以通过标注类的属性来给返回字段加说明。也比如说,对于swagger,不推荐在使用GET方式提交数据的时候还使用Body,仅推荐使用query参数、header参数或者路径参数,当然了这个限制只适用于在线调试。
- `没有接口文档更新管理`,虽然一个接口更新之后,可能不会关心旧版的接口信息,但你“可能”想看看旧版的接口信息,例如有些灰度更新发布的时候可能还会关心旧版的接口。那么此时只能由后端去看看有没有注释留下了,所以可以考虑接口文档大更新的时候注释旧版的,然后写下新版的。【当然这个问题可以通过导出接口文档来对比。】
- 虽然现在Java的实体类中有不少模型,po,dto,vo等,模型的区分是为了屏蔽一些多余参数,比如一个用户登录的时候只需要username,password,但查权限的时候需要连接上权限表的信息,而如果上述两个操作都是使用了User这个实体的话,在文档中就会自动生成了多余的信息,这就要求了你基于模型来创建多个实体类,比如登录的时候一个LoginForm,需要用户-权限等信息的时候才使用User类。(当然了,这个问题等你会swagger之后你就大概就会怎么规避这个问题了。)
> 不明白?无妨,我们继续看
## 建立Swagger项目demo
利用`Spring Initializer`构建SpringBoot整合Swagger项目。
- 项目文件结构图
```
swagger-demo
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.md
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ ├── config
│ │ │ └── SwaggerConfig.java
│ │ ├── controller
│ │ │ ├── RoleController.java
│ │ │ └── UserController.java
│ │ ├── domain
│ │ │ └── LoginForm.java
│ │ └── SwaggerDemoApplication.java
│ └── resources
│ └── application.yml
└── test
└── java
└── com
└── example
└── SwaggerDemoApplicationTests.java
```
- pom.xml (只展示Swagger依赖配置部分)
```xml
<!--Swagger依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.7</version>
</dependency>
```
- application.yaml
```yaml
spring:
profiles:
# active: release
active: dev
```
要使用swagger,须对Swagger进行配置。因此,需要创建一个Swagger配置类,一般命名为SwaggerConfig.java
- SwaggerConfig.java
```java
package com.zhoujk.swagger.config;
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.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration //表明是配置类
@EnableSwagger2 //开启swagger功能
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
//调用apiInfo方法,创建一个ApiInfo实例,展示文档页面信息内容
.apiInfo(apiInfo())
// select()函数返回一个ApiSelectorBuilder实例,用来控制接口被swagger做成文档
.select()
// 用于指定扫描哪个包下的的接口
.apis(RequestHandlerSelectors.basePackage("com.zhoujk.swagger"))
// 选择所有的API,如果你只想为部分API生成文档,可以在这里配置
.paths(PathSelectors.any())
.build();
}
//构建api文档的详细信息函数
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//页面标题
.title("Swagger演示项目API")
//项目描述
.description("Swagger演示项目API接口界面")
//服务条款网址
.termsOfServiceUrl("https://zhoujk.top")
//作者信息
.contact(new Contact("千叶", "zhoujk.top", "zhou431613@163.com"))
//许可证
.license("Apache 3.0")
//版本
.version("1.0")
.build();
}
}
```
其中==@EnableSwagger2==表示开启Swagger2的功能。在Config配置类中需要注入一个Docket的Bean,该Bean包含了ApiInfo,即基本的API文件的描述信息,以及包扫描的基本包名等信息。其中`select`、`apis`、`paths`和`build`这四个是一起的,它们组合最后才能返回一个Docket对象,当然apis、paths是可选的,非必选,apis表示可以扫描哪个路径下的实例,path表示过滤的方式。
- 实体类LoginForm
```java
package com.zhoujk.swagger.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "用户登录表单对象",description = "用户登录表单对象")
public class LoginForm {
@ApiModelProperty(value = "用户名", required = true, example = "admin")
private String username;
@ApiModelProperty(value = "密码", required = true, example = "123456")
private String password;
private String getUsername() {
return username;
}
private String getPassword() {
return password;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
}
```
在这里,实体类LoginForm也作为请求参数。因此,对于实体类也要进行相关的注解。
此时我们需要使用`@ApiModel`来标注实体类,然后在接口中定义入参为实体类即可:
- @ApiModel:用来标注类
- 常用配置项:
- value:实体类简称
- description:实体类说明
- @ApiModelProperty:用来描述类的字段的意义。
- 常用配置项:
- value:字段说明
- example:设置请求示例(Example Value)的默认值,如果不配置,当字段为string的时候,此时请求示例中默认值为"".
- name:用新的字段名来替代旧的字段名。
- allowableValues:限制值得范围,例如`{1,2,3}`代表只能取这三个值;`[1,5]`代表取1到5的值;`(1,5)`代表1到5的值,不包括1和5;还可以使用infinity或-infinity来无限值,比如`[1, infinity]`代表最小值为1,最大值无穷大。
- required:标记字段是否必填,默认是false,
- hidden:用来隐藏字段,默认是false,如果要隐藏需要使用true,因为字段默认都会显示,就算没有`@ApiModelProperty`。
接着下来,我来编写Controller类。
- userController类
```java
package com.zhoujk.swagger.controller;
import com.zhoujk.swagger.domain.LoginForm;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
/**
* @author : zhoujiankang
* @Desc:
* @since : 2022/4/4 16:34
*/
@Api(tags = "用户管理")
@RestController
public class UserController {
@GetMapping("/info")
public String info(String id) {
return "1811421199";
}
@ApiOperation(value = "用户测试", notes = "用户测试notes", tags = "角色管理")
@GetMapping("/test")
public String test(String id) {
return "test";
}
//获取实体类对象参数
@ApiOperation(value = "登录接口", notes = "登录接口的说明")
@PostMapping("/login")
public LoginForm login(@RequestBody LoginForm loginForm) {
return loginForm;
}
//使用URL query参数
@ApiOperation(value = "登录接口2", notes = "登录接口说明2")
@ApiImplicitParams({@ApiImplicitParam(name = "username",
value = "用户",
required = true,
paramType = "query",
defaultValue = "root"),
@ApiImplicitParam(name = "password",
value = "密码",
required = true,
paramType = "query")})
@PostMapping(value = "/login2")
public LoginForm login2(String username, String password) {
System.out.println(username + ":" + password);
LoginForm loginForm = new LoginForm();
loginForm.setUsername(username);
loginForm.setPassword(password);
return loginForm;
}
//使用路劲参数
@PostMapping("/login3/{id1}/{id2}")
@ApiOperation(value = "登录接口3", notes = "登录接口3的说明")
@ApiImplicitParams({@ApiImplicitParam(name = "id1",
value = "用户名",
required = true,
paramType = "path"),
@ApiImplicitParam(name = "id2",
value="密码",
required = true,
paramType = "path")})
public String login3(@PathVariable Integer id1, @PathVariable Integer id2) {
return id1 + ":" + id2;
}
// 使用header传递参数
@PostMapping("/login4")
@ApiOperation(value = "登录接口4", notes = "登录接口4的说明")
@ApiImplicitParams({@ApiImplicitParam(name = "username",
value="用户名",
required = true,
paramType = "header"),
@ApiImplicitParam(name = "password",
value="密码",
required=true,
paramType="header")})
public String longin4(@RequestHeader String username, @RequestHeader String password) {
return username + ":" + password;
}
//对于这个接口的在线测试,是基于弃用了swagger-bootstrap-ui之后。
// 默认情况下,sagger无法选择发送数据的编码方式
@PostMapping("login5")
@ApiOperation(value = "等录接口5", notes = "登录接口5的说明")
@ApiImplicitParams({@ApiImplicitParam(name = "username",
value = "用户名",
required = true,
paramType = "form"),
@ApiImplicitParam(name = "password",
value = "密码",
required=true,
paramType = "form")}
)
public String login5(String username, String password) {
return username + ":" + password;
}
// 有文件上传时要用@ApiParam,用法基本与@ApiImplicitParam一样,不过@ApiParam用在参数上
// 或者你也可以不注解,swagger会自动生成说明
@ApiOperation(value = "上传文件", notes = "上传文件")
@PostMapping(value = "/upload")
public String upload(@ApiParam(value = "图片文件", required = true) MultipartFile uploadFile) {
return uploadFile.getOriginalFilename();
}
// 多个文件上传时,**swagger只能测试单文件上传**
@ApiOperation(value = "上传多个文件", notes = "上传多个文件")
@PostMapping(value = "upload2", consumes = "multipart/*", headers = "content-type=multipart/form-data")
public String upload2(@ApiParam(value = "图片文件", required = true, allowMultiple = true) MultipartFile[] uploadFile) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < uploadFile.length; i++) {
System.out.println(uploadFile[i].getOriginalFilename());
stringBuilder.append(uploadFile[i].getOriginalFilename());
stringBuilder.append(",");
}
return stringBuilder.toString();
}
// 既有文件,又有参数
@ApiOperation(value = "既有文件,又有参数",notes = "既有文件,又有参数")
@PostMapping(value = "/upload3")
@ApiImplicitParams({
@ApiImplicitParam(name = "name",
value = "图片新名字",
required = true
)
})
public String upload3(@ApiParam(value = "图片文件", required = true)MultipartFile uploadFile,
String name){
String originalFilename = uploadFile.getOriginalFilename();
return originalFilename+":"+name;
}
// 如果需要额外的参数,非本方法用到,但过滤器要用,类似于权限token
@PostMapping("/login6")
@ApiOperation(value = "带token的接口",notes = "带token的接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "authorization",//参数名字
value = "授权token",//参数的描述
required = true,//是否必须传入
paramType = "header"
)
,
@ApiImplicitParam(name = "username",//参数名字
value = "用户名",//参数的描述
required = true,//是否必须传入
paramType = "query"
)
})
public String login6(String username){
return username;
}
}
```
接口有时候应该是分组的,而且大部分都是在一个controller中的,比如用户管理相关的接口应该都在UserController中,那么不同的业务的时候,应该定义/划分不同的接口组。接口组可以使用`@Api`来划分。
比如:
```java
@Api(tags = "用户管理") // tags:你可以当作是这个组的名字。
@RestController
public class UserController {
```
和
```java
@Api(tags = "用户管理") // tags:你可以当作是这个组的名字。
@RestController
public class UserController {
}
```
- RoleController类
```java
package com.zhoujk.swagger.controller;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPObject;
import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonArrayFormatVisitor;
import com.sun.org.apache.xerces.internal.xs.StringList;
import com.zhoujk.swagger.domain.LoginForm;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import netscape.javascript.JSObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.spring.web.json.Json;
import java.util.ArrayList;
/**
* @author : zhoujiankang
* @Desc:
* @since : 2022/4/4 16:34
*/
@Api(tags = "角色管理")
@RestController
public class RoleController {
// 返回被@ApiModel标注的类的对象
@ApiOperation(value = "实体类响应", notes = "返回数据为实体类的接口")
@PostMapping("/role1")
public LoginForm role1(@RequestBody LoginForm loginForm) {
return loginForm;
}
@ApiOperation(value = "非实体类", notes = "非实体类")
@ApiResponses({
@ApiResponse(code = 200, message = "调用成功"),
@ApiResponse(code = 401, message = "无权限")
})
@PostMapping("/role2")
public String role2() {
JSONArray jsonArray = new JSONArray();
JSONObject jsonObject = new JSONObject();
jsonObject.put("provinces","浙江");
jsonObject.put("city", new String[] {"杭州","温州","绍兴","宁波","台州","嘉兴","金华","衢州"});
jsonArray.add(jsonObject);
return jsonArray.toJSONString();
}
}
```
我们再来看看接口,使用`@ApiOperation`来描述接口,比如:
```java
@ApiOperation(value = "非实体类", notes = "非实体类")
@ApiResponses({
@ApiResponse(code = 200, message = "调用成功"),
@ApiResponse(code = 401, message = "无权限")
})
@PostMapping("/role2")
public String role2() {
```
![image-20220407193325334](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220407193325334.png)
关于Swagger的注解,统计一下,一共使用多少个?又都用在什么地方呢?
| 参数 | 说明 | 使用场景案例 |
| :----------------: | :--------------------------------------------------: | :----------------------------------------------------------: |
| @Api | 用于类,标识这个类是Swagger的资源,通常为Controller | @Api(tags = "角色管理") |
| @ApiOperation | 用于方法,描述Controller类中的method接口 | // 返回被@ApiModel标注的类的对象 @ApiOperation(value = "实体类响应", notes = "返回数据为实体类的接口") |
| @ApiResponses | 用于方法,包含多个@ApiResponse | @ApiResponses({@ApiResponse(code = 200, message = "调用成功"), @ApiResponse(code = 401, message = "无权限") }) |
| @ApiResponse | 用于方法,描述单个响应信息 | - |
| @EnableSwagger2 | 开启Swagger2的功能 | @Configuration //表明是配置类 @EnableSwagger2 //开启swagger功能 public class SwaggerConfig {......} |
| @ApiImplicitParams | 用于方法,包含多个@ApiImplicitParam | @ApiImplicitParams({@ApiImplicitParam(name = "username", value="用户名", required = true, paramType = "header"), @ApiImplicitParam(name = "password", value="密码", required=true, paramType="header")}) |
| @ApiImplicitParam | 用于方法,表示单独的请求参数 | - |
| @ApiModel | 用于Model类,标识Swagger model,提供对象额外信息描述 | @ApiModel(value = "用户登录表单对象",description = "用户登录表单对象") |
| @ApiModelProperty | 用于Model属性,描述属性,设置是否必须 | @ApiModelProperty(value = "用户名", required = true, example = "admin") |
想一想,Swagger的注解都全部使用到了吗?如果没有,又是那些注解没有使用呢?
| 参数 | 说明 |
| ---------- | ---------------------------------------- |
| @ApiIgnore | 用于类,忽略该Controller,即不扫描当前类 |
| @ApiError | 用于方法,接口错误所返回的信 |
再想一想,这些注解需要写吗?不写又怎样?
![image-20220408213212290](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408213212290.png)
Added support for resolving properties in property sources to replace expression in certain annnotations。这句话什么意思?添加了对解析属性源中的属性以替换某些注解中的表达式的支持。可以简单的理解为,我们可以对类和方法添加描述。例如:为get请求 /test方法操作添加描述。
![image-20220408214443298](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408214443298.png)
这里是Springfox-demos的一些案例:https://gitee.com/zhou431615/springfox-demos.git,可以参考。网上的一些例子,大都相同,可以去看看springfox创始人写的demo。For example,
```java
package springfoxdemo.boot.swagger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.AuthorizationScopeBuilder;
import springfox.documentation.builders.ImplicitGrantBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.BasicAuth;
import springfox.documentation.service.Contact;
import springfox.documentation.service.GrantType;
import springfox.documentation.service.LoginEndpoint;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger.web.SecurityConfiguration;
import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
import springfox.documentation.swagger1.annotations.EnableSwagger;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.petstore.controller.PetController;
import springfox.petstore.controller.PetStoreResource;
import springfox.petstore.controller.UserController;
import springfoxdemo.boot.swagger.web.HomeController;
import javax.print.Doc;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import static springfox.documentation.builders.PathSelectors.*;
@SpringBootApplication
@EnableSwagger //Enable swagger 1.2 spec
@EnableSwagger2 //Enable swagger 2.0 spec
@EnableOpenApi //Enable open api 3.0.3 spec
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
@Bean
public PetController petController() {
return new PetController();
}
@Bean
public PetStoreResource petStoreController() {
return new PetStoreResource();
}
@Bean
public UserController userController() {
return new UserController();
}
@Bean
public Docket petApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("full-petstore-api")
.apiInfo(apiInfo())
.select()
.paths(petstorePaths())
.build()
.securitySchemes(Collections.singletonList(oauth()))
.securityContexts(Collections.singletonList(securityContext()));
}
@Bean
public Docket categoryApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("category-api")
.apiInfo(apiInfo())
.select()
.paths(categoryPaths())
.build()
.ignoredParameterTypes(ApiIgnore.class)
.enableUrlTemplating(true);
}
@Bean
public Docket multipartApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("multipart-api")
.apiInfo(apiInfo())
.select()
.paths(multipartPaths())
.build();
}
private Predicate<String> categoryPaths() {
return regex(".*/category.*")
.or(regex(".*/category")
.or(regex(".*/categories")));
}
private Predicate<String> multipartPaths() {
return regex(".*/upload.*");
}
@Bean
public Docket userApi() {
AuthorizationScope[] authScopes = new AuthorizationScope[1];
authScopes[0] = new AuthorizationScopeBuilder()
.scope("read")
.description("read access")
.build();
SecurityReference securityReference = SecurityReference.builder()
.reference("test")
.scopes(authScopes)
.build();
List<SecurityContext> securityContexts =
Collections.singletonList(
SecurityContext.builder()
.securityReferences(Collections.singletonList(securityReference))
.build());
return new Docket(DocumentationType.SWAGGER_2)
.securitySchemes(Collections.singletonList(new BasicAuth("test")))
.securityContexts(securityContexts)
.groupName("user-api")
.apiInfo(apiInfo())
.select()
.paths(input -> input.contains("user"))
.build();
}
@Bean
public Docket openApiPetStore() {
return new Docket(DocumentationType.OAS_30)
.groupName("open-api-pet-store")
.select()
.paths(petstorePaths())
.build();
}
private Predicate<String> petstorePaths() {
return regex(".*/api/pet.*")
.or(regex(".*/api/user.*")
.or(regex(".*/api/store.*")));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Springfox petstore API")
.description("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum " +
"has been the industry's standard dummy text ever since the 1500s, when an unknown printer "
+ "took a " +
"galley of type and scrambled it to make a type specimen book. It has survived not only five " +
"centuries, but also the leap into electronic typesetting, remaining essentially unchanged. " +
"It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum " +
"passages, and more recently with desktop publishing software like Aldus PageMaker including " +
"versions of Lorem Ipsum.")
.termsOfServiceUrl("http://springfox.io")
.contact(new Contact("springfox", "", ""))
.license("Apache License Version 2.0")
.licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
.version("2.0")
.build();
}
@Bean
SecurityContext securityContext() {
AuthorizationScope readScope = new AuthorizationScope("read:pets", "read your pets");
AuthorizationScope[] scopes = new AuthorizationScope[1];
scopes[0] = readScope;
SecurityReference securityReference = SecurityReference.builder()
.reference("petstore_auth")
.scopes(scopes)
.build();
return SecurityContext.builder()
.securityReferences(Collections.singletonList(securityReference))
.forPaths(ant(".*/api/pet.*"))
.build();
}
@Bean
SecurityScheme oauth() {
return new OAuthBuilder()
.name("petstore_auth")
.grantTypes(grantTypes())
.scopes(scopes())
.build();
}
@Bean
SecurityScheme apiKey() {
return new ApiKey("api_key", "api_key", "header");
}
List<AuthorizationScope> scopes() {
return Arrays.asList(
new AuthorizationScope("write:pets", "modify pets in your account"),
new AuthorizationScope("read:pets", "read your pets"));
}
List<GrantType> grantTypes() {
GrantType grantType = new ImplicitGrantBuilder()
.loginEndpoint(new LoginEndpoint("http://petstore.swagger.io/api/oauth/dialog"))
.build();
return Collections.singletonList(grantType);
}
@Bean
public SecurityConfiguration securityInfo() {
return SecurityConfigurationBuilder.builder()
.clientId("abc")
.clientSecret("123")
.realm("pets")
.appName("petstore")
.scopeSeparator(",")
.build();
}
}
```
仔细看一下这一段代码,数一数,一共创建了多少个`Docket`?
![image-20220408215717843](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408215717843.png)
![image-20220408215947467](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408215947467.png)
### ApiPost介绍
在这里简单介绍一下,ApiPost,使用步骤与Postman并无多大区别,对于新手友好上手。下载地址:https://www.apipost.cn/
APlPost = Postman + Swagger + Mock后端、前端、测试同时在线编辑,内容实时同步。
![image-20220408175552920](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408175552920.png)
> 国内包括我在内的开发者,之前进行API调试时一直在使用Postman。它的接口调试功能虽然强大,但是很多诸如生成接口文档等研发常用功能并不符合国人习惯;虽然支持协同功能,但几乎是天价,同时由于服务器在国外,经常出现延迟甚至卡顿的情况,十分不稳定。做Apipost的初心是简单易用,上手要快,支持研发协同、快速导出接口文档;并针对个人开发者,完全支持离线使用。Apipost已迭代到6.0版本,我们希望通过这款产品,让更多的中国开发者从无尽的消耗中解放出来,更希望以此证明中国软件的价值和中国力量的崛起 ----ApiPost创始人穆红伟评语
我很喜欢使用ApiPost的分享文档的功能,也可以导出文档。
![image-20220408181243663](https://mybolg-typora.oss-cn-beijing.aliyuncs.com/image-20220408181243663.png)
## 参考文档
1.SpringFox[官方参考文档](https://springfox.github.io/springfox/)
2.SpringFox[官方文档中文翻译版](https://uhfun.gitee.io/uhfun-jekyll/tech/2020/05/13/Springfox%E5%8F%82%E8%80%83%E6%96%87%E6%A1%A3-Springfox-Reference-Documentation%E4%B8%AD%E6%96%87%E7%BF%BB%E8%AF%91.html#1-%E4%BB%8B%E7%BB%8D)
3.Swagger [OpenAPIGuide](https://swagger.io/docs/specification/2-0/basic-structure/)
### 名词解释
REST API
REST 是一组架构规范,并非协议或标准。API 开发人员可以采用各种方式实施 REST。当客户端通过 RESTful API 提出请求时,它会将资源状态表述传递给请求者或终端。该信息或表述通过 HTTP 以下列某种格式传输:JSON(Javascript 对象表示法)、HTML、XLT、Python、PHP 或纯文本。JSON 是最常用的编程语言,尽管它的名字英文原意为“JavaScript 对象表示法”,但它适用于各种语言,并且人和机器都能读。
还有一些需要注意的地方:头和参数在 RESTful API HTTP 请求的 HTTP 方法中也很重要,因为其中包含了请求的元数据、授权、统一资源标识符(URI)、缓存、cookie 等重要标识信息。有请求头和响应头,每个头都有自己的 HTTP 连接信息和状态码。
Springfox-Swagger-ui学习笔记