Springboot入门教程(7)-整合springdoc-openapi-ui(上)

说起生成Api文档的库,很多人应该都知道Swagger,百度后发现原本springboot整合swagger的库用的比较多的是springfox。但是因为这个框架更新不是很及时,所以我再次百度后发现了有人建议使用springdoc-openapi这个库。Github上的地址是https://github.com/springdoc/springdoc-openapi,发现这个库确实代码更新,所以决定尝试使用这个库来生成swagger的api文档。
因为要讲的内容有点多,所以我会分成上、下两篇来写。

  1. 首先依然是先要在build.gradle的dependencies中添加依赖包
implementation "org.springdoc:springdoc-openapi-ui:1.5.9"
  1. 默认的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画面

代表可以成功使用swagger了。
这里我们可以看到,其实真实的访问路径是被重定向到http://localhost:8080/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config。所以排除的路径中我把包含swagger和api-docs的路径都排除了。

  1. 这时候也可以在application.properties中添加一些配置,具体可以参考Spring Boot 整合 springdoc-openapi中的配置。
    这里我只加一行把/swagger-ui.html这个默认路径修改成比较方便访问的路径。
springdoc.swagger-ui.path=/api-docs

这样就变成了可以用http://localhost:8080/api-docs这个较短的路径来访问了。

  1. 再来,我们可以看到这个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画面

标题区域已经按照我们自定义的展示了。
到这里,算是把swagger的功能加进来了。

接下来就是让这份API文档按照我们希望的格式来展现了。

  1. 首先,如果按照我之前的教程走写代码的可能会发现teacher-controller中的teacher/teacherDetail和subject-controller中的subject/subjects的api在这里变成了每种方式一个,一共有7个。这是怎么回事呢?我们查看controller的代码发现这两个api我用的都是@RequestMapping,这就没有指定请求的方式,所以swagger就直接给你生成了每一种方式都有。
    那我原本是希望用get来访问的,所以就可以改成@GetMapping再看一下


    修改请求方式后

    这样就看到都变成get访问的了。

  2. 接着我们就尝试使用springdoc-openapi的一些注解来生成我们需要的api文档格式。关于一些注解从swagger2迁移到swagger3及其用法的简单说明可以查看Swagger3 注解使用(Open API 3),以及也可以参考官方提供的demo。
    先提一个我在使用过程中发现的点:
    我在写这篇博文的时候使用的是最新的1.5.9的版本,这时我发现在login的api上我仅使用了@PostMapping(value = "/login")这样的注解代码,在swagger界面生成的api就已经是参数访问的形式了。

    1.5.9版本

    但是之前我使用过1.5.4版本,这样写运行的结果却是默认要传json格式的参数,如图
    1.5.4版本

    而要把它变成上图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;
    }
}

运行之后就得到如下结果


user api 1

user api 2

这里我们给这个controller类加了@Tag,定义了名称是user,加了描述。
然后给login这个api加了@Operation注解,加了summary描述api的用途。
最后一个重要的是@ApiResponses这个注解,它可以定义api返回的样式。其中返回的Example Value默认会根据你返回的entity格式生成,例如我这边返回的entity是ResponseData,它定义了code、message和data,所以默认的返回样式就会变成


默认的ResponseData返回格式

而现在我给login的api加了指定的examples,它就按照我指定的显示了。
  1. 接着又有个问题,我的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的请求头参数。运行之后结果如下


api加上统一请求头

代码依旧可以参考我在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)

你可能感兴趣的:(Springboot入门教程(7)-整合springdoc-openapi-ui(上))