说起生成Api文档的库,很多人应该都知道Swagger,百度后发现原本springboot整合swagger的库用的比较多的是springfox。但是因为这个框架更新不是很及时,所以我再次百度后发现了有人建议使用springdoc-openapi这个库。Github上的地址是https://github.com/springdoc/springdoc-openapi,发现这个库确实代码更新,所以决定尝试使用这个库来生成swagger的api文档。
因为要讲的内容有点多,所以我会分成上、下两篇来写。
- 首先依然是先要在build.gradle的dependencies中添加依赖包
implementation "org.springdoc:springdoc-openapi-ui:1.5.9"
- 默认的swagger访问路径是/swagger-ui.html,但是这时候我们尝试使用http://localhost:8080/swagger-ui.html来访问的话会提示401,这是因为登录验证的拦截器在起作用。来到WebConfigurer的addInterceptors方法中,原本我们设置了排除/login和/register两个路径,现在要把swagger的路径也排除。因此将代码改成如下:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/login", "/register", "/images/**",
"/swagger**/**","/**/api-docs/**");
}
这时再重新启动访问http://localhost:8080/swagger-ui.html就可以看到如下画面
代表可以成功使用swagger了。
这里我们可以看到,其实真实的访问路径是被重定向到http://localhost:8080/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config。所以排除的路径中我把包含swagger和api-docs的路径都排除了。
- 这时候也可以在application.properties中添加一些配置,具体可以参考Spring Boot 整合 springdoc-openapi中的配置。
这里我只加一行把/swagger-ui.html这个默认路径修改成比较方便访问的路径。
springdoc.swagger-ui.path=/api-docs
这样就变成了可以用http://localhost:8080/api-docs这个较短的路径来访问了。
- 再来,我们可以看到这个swagger的界面标题还是OpenAPI definition v0,那我需要把它改成我自己系统标题和版本号需要怎么做呢?同样是在WebConfigurer中配置,添加如下代码:
@Bean
public OpenAPI openAPI(@Value("${springdoc.version}") String appVersion) {
return new OpenAPI()
.info(new Info()
.title("Student Manager API")
.description("Student manager server api.学生管理系统后台API.")
.version(appVersion)
.license(new License()
.name("Apache2.0")
.url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("Documentation")
.url("https://www.jianshu.com/nb/41542276"));
}
这里我们配置了一个自定义的配置参数springdoc.version,所以需要把这个加到application.properties中
springdoc.version=1.0
这时再运行可以看到如下画面
标题区域已经按照我们自定义的展示了。
到这里,算是把swagger的功能加进来了。
接下来就是让这份API文档按照我们希望的格式来展现了。
-
首先,如果按照我之前的教程走写代码的可能会发现teacher-controller中的teacher/teacherDetail和subject-controller中的subject/subjects的api在这里变成了每种方式一个,一共有7个。这是怎么回事呢?我们查看controller的代码发现这两个api我用的都是@RequestMapping,这就没有指定请求的方式,所以swagger就直接给你生成了每一种方式都有。
那我原本是希望用get来访问的,所以就可以改成@GetMapping再看一下
这样就看到都变成get访问的了。
-
接着我们就尝试使用springdoc-openapi的一些注解来生成我们需要的api文档格式。关于一些注解从swagger2迁移到swagger3及其用法的简单说明可以查看Swagger3 注解使用(Open API 3),以及也可以参考官方提供的demo。
先提一个我在使用过程中发现的点:
我在写这篇博文的时候使用的是最新的1.5.9的版本,这时我发现在login的api上我仅使用了@PostMapping(value = "/login")这样的注解代码,在swagger界面生成的api就已经是参数访问的形式了。
但是之前我使用过1.5.4版本,这样写运行的结果却是默认要传json格式的参数,如图
而要把它变成上图1.5.9版本的样子,需要把注解代码改成
@PostMapping(value = "/login", consumes = { "application/x-www-form-urlencoded" })
这是一个要注意的地方。
然后我们把UserController的代码改成如下
@Tag(name = "user", description = "the user API")
@RestController
public class UserController {
private String admin = "admin";
private String psd = "qwertyuiop";
@Resource
private RedisUtil redisUtil;
@GetMapping(value = "/getUser")
String getUser(String username){
return "Hello, " + username;
}
@Operation(summary = "Logs user into the system")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "successful operation",
content = { @Content(mediaType = "application/json",schema = @Schema(implementation = ResponseData.class),
examples = {@ExampleObject(value = "{\n" +
" \"message\": \"Ok\",\n" +
" \"code\": 200,\n" +
" \"data\": {\n" +
" \"username\": \"admin\",\n" +
" \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9......-2Xz7JH9Ilg_SVbQ\"\n" +
" }\n" +
"}")})})})
@PostMapping(value = "/login")
public ResponseData login(final String username, final String password)
{
ResponseData responseData = ResponseData.ok();
if(username.equals(admin) && password.equals(psd)){
Map map = new HashMap();
map.put("username", username);
String token = JWTUtil.genToken(map, new Date(System.currentTimeMillis() + 60L* 1000L * 3L));
redisUtil.set(username, token, 60 * 60 * 2);
//封装成对象返回给客户端
responseData.putDataValue("username", username);
responseData.putDataValue("token", token);
}
else{
responseData = ResponseData.customerError();
}
return responseData;
}
}
运行之后就得到如下结果
这里我们给这个controller类加了@Tag,定义了名称是user,加了描述。
然后给login这个api加了@Operation注解,加了summary描述api的用途。
最后一个重要的是@ApiResponses这个注解,它可以定义api返回的样式。其中返回的Example Value默认会根据你返回的entity格式生成,例如我这边返回的entity是ResponseData,它定义了code、message和data,所以默认的返回样式就会变成
而现在我给login的api加了指定的examples,它就按照我指定的显示了。
- 接着又有个问题,我的teacher和subject的api都是需要在header中携带登录信息访问的。就是我需要给除了login的其他请求都统一带上请求头参数,所以同样参考了Spring Boot 整合 springdoc-openapi中的做法,我在WebConfigurer中又做了如下修改:
@Bean
public OpenAPI openAPI(@Value("${springdoc.version}") String appVersion) {
Components components = new Components();
components
.addParameters("token", new HeaderParameter().required(true).name("token").schema(new StringSchema()).required(true))
.addParameters("username", new HeaderParameter().required(true).name("username").schema(new StringSchema()).required(true));
return new OpenAPI()
.components(components)
.info(new Info()
.title("Student Manager API")
.description("Student manager server api.学生管理系统后台API.")
.version(appVersion)
.license(new License()
.name("Apache2.0")
.url("http://springdoc.org")))
.externalDocs(new ExternalDocumentation()
.description("Documentation")
.url("https://www.jianshu.com/nb/41542276"));
}
定义两个请求头的参数名称分别为token和username,然后把这两个请求头参数统一添加到每个api中
/**
* 添加全局的请求头参数
*/
@Bean
public OpenApiCustomiser customerGlobalHeaderOpenApiCustomiser() {
return openApi -> openApi.getPaths().values().stream().flatMap(pathItem -> pathItem.readOperations().stream())
.forEach(operation -> {
String summary = operation.getSummary();
if(summary != null){
if(!summary.equals("Logs user into the system"))
operation.addParametersItem(new HeaderParameter().$ref("#/components/parameters/username"))
.addParametersItem(new HeaderParameter().$ref("#/components/parameters/token"));
}else{
operation.addParametersItem(new HeaderParameter().$ref("#/components/parameters/username"))
.addParametersItem(new HeaderParameter().$ref("#/components/parameters/token"));
}
});
}
这里要考虑的是要把login的api排除掉,所以我利用了上面给login的api加的summary,判断这个api不是login的api时就加上username和token的请求头参数。运行之后结果如下
代码依旧可以参考我在github上面的代码https://github.com/ahuadoreen/studentmanager,请注意的是这份代码使用的是1.5.4的版本,和文中引用的1.5.9略有差别,文中也有指出。
参考文档
springdoc-openapi github
springdoc-openapi官方文档
Spring Boot 整合 springdoc-openapi
Swagger3 注解使用(Open API 3)