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<String, String> map = new HashMap<String, String>();
            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)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容