zoukankan      html  css  js  c++  java
  • Spring 5 中函数式webmvc开发中的swagger文档

    Spring 5 中一个非常重要的更新就是增加了响应式web开发WebFlux,并且推荐使用函数式风格(RouterFunctionHandlerFunction)来开发WebFlux。对于之前主流的MVC开发模式,Spring也顺道给它提供了和WebFlux函数式开发几乎一致的方式(见上文《Spring 5 MVC 中的 Router Function 使用》)。这样,响应式WebFlux和非响应式MVC都可以通过Controller来实现,也都可以通过RouterFunction来实现。

    但是Spring推荐使用函数式方式;而且听说在所有场景也推荐使用WebFlux,后续有可能废弃掉非响应式MVC

    对于通过Controller来实现的接口,swagger可以直接扫描处理。使用了RouterFunction的怎么办呢?这篇文章我们来看一下如果通过springdoc这个项目来实现函数式接口的文档生成。

    pom依赖

    假设你使用的是maven。如果使用gradle我估计也差不多

    要使用springdoc-openapi,需要添加它的依赖。一般需要两个,第一个是基础公共依赖:

    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-ui</artifactId>
        <version>1.5.9</version>
    </dependency>
    

    另一个根据对象不同需要引入的也不同。我们这里先处理非响应式MVC的接口,需要依赖

    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-webmvc-core</artifactId>
        <version>1.5.9</version>
    </dependency>
    

    依赖的版本号可以自己去查一下最新的

    RouterOperation

    接下来需要去RouterFunction上面定义swagger的显示信息,在controller中使用的注解是io.swagger.v3.oas.annotations.Operation(如果是版本2使用的注解是io.swagger.annotations.ApiOperation),现在需要使用org.springdoc.core.annotations.RouterOperation。以《Spring 5 MVC 中的 Router Function 使用》中的接口为例,需要这样写

    @Configuration
    public class RoutingConfig {
    
        @Bean
        @RouterOperations({
                @RouterOperation(
                        path = "/model/building/{entId}/stations",
                        beanClass = ModelBuildingHandler.class,
                        beanMethod = "getStations",
                        method = RequestMethod.GET,
                        operation = @Operation(
                                operationId = "getStations",
                                parameters = @Parameter(
                                        name = "entId",
                                        in = ParameterIn.PATH,
                                        required = true,
                                        description = "企业ID"
                                )
                        )
                )
        })
        public RouterFunction<ServerResponse> getModelBuildingRouters(ModelBuildingHandler modelBuildingHandler) {
            return RouterFunctions.nest(path("/model/building"),
                    RouterFunctions.route(GET("/{entId}/stations"), modelBuildingHandler::getStations)
                            .andRoute(GET("/{stationId}/device-types"), modelBuildingHandler::getDeviceTypes)
            );
        }
    }
    

    不同于Controller中的接口会被自动发现,这里虽然有两个方法但是我们只定义了一个RouterOperation操作,会导致看不到第二个方法。

    我们修改一下application.properties就能看到效果了:

    springdoc.packagesToScan=要扫描的包
    springdoc.pathsToMatch=/**
    

    注意springdoc.packagesToScan这里,需要写一个包含了RouterFunctionHandlerFunction实现的包。我当时只写了RouterFunction所在的包,一直不成功

    再响应增加一个RouterOperation即可:

    @RouterOperations({
            @RouterOperation(
                    path = "/model/building/{entId}/stations",
                    beanClass = ModelBuildingHandler.class,
                    beanMethod = "getStations",
                    method = RequestMethod.GET,
                    operation = @Operation(
                            operationId = "getStations",
                            parameters = @Parameter(
                                    name = "entId",
                                    in = ParameterIn.PATH,
                                    required = true,
                                    description = "企业ID"
                            )
                    )
            ),
            @RouterOperation( // 这里我省略了一些属性配置
                    path = "/model/building/{stationId}/device-types",
                    beanClass = ModelBuildingHandler.class,
                    beanMethod = "getDeviceTypes"
            )
    })
    

    RequestBody

    上面讲的都是GET的请求,如果需要设置请求体格式怎么办呢?

    在RouterFunction中增加一个POST请求(下面最后一个url):

    public RouterFunction<ServerResponse> getModelBuildingRouters(ModelBuildingHandler modelBuildingHandler) {
      return RouterFunctions.nest(
              path("/model/building"),
              RouterFunctions.route(GET("/{entId}/stations"), modelBuildingHandler::getStations)
                      .andRoute(GET("/{stationId}/device-types"), modelBuildingHandler::getDeviceTypes)
                      .andRoute(POST("/devices/points/real-time-data"), modelBuildingHandler::getRealTimeData)
      );
    }
    

    它对应的RouterOperation如下:

    @RouterOperation(
            path = "/model/building/devices/points/real-time-data",
            beanClass = ModelBuildingHandler.class,
            beanMethod = "getRealTimeData",
            operation = @Operation(
                    operationId = "getRealTimeData",
                    requestBody = @RequestBody(
                            required = true,
                            description = "请求体",
                            content = @Content(
                                    schema = @Schema(implementation = RealTimeDataQueryVO.class)
                            )
                    )
            )
    )
    

    要在Handler中拿到请求参数,使用body方法:RealTimeDataQueryVO queryVo = req.body(RealTimeDataQueryVO.class);

    public ServerResponse getRealTimeData(ServerRequest req) {
        RealTimeDataQueryVO queryVo;
        try {
            queryVo = req.body(RealTimeDataQueryVO.class);
            log.info("请求参数{}", queryVo);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        RealTimeDataResultBO resultBo = modelBuildingService.getRealTimeData(transferRealTimeDataQueryParam(queryVo));
        return body(RdfaResult.success(transferRealTimeDataResult(resultBo)));
    }
    

    上面就是如何在函数式MVC编程中使用swagger。可以看到比较繁琐,3行代码对应了差不多30行swagger配置。

    从swagger v2 迁移

    如果之前的项目使用的是springfox之类的swagger版本2,需要将其依赖移除,并修改类上的注解。

    如何修改可以参考 https://springdoc.org/#migrating-from-springfox

    主要的变更点如下图:

    Swagger配置

    这一步是可选的,几乎没什么必要。

    可以像之前一样定义文档的归属、协议、版本等等:

    @Configuration
    public class DocConfig {
        @Bean
        public OpenAPI springShopOpenApi() {
            return new OpenAPI()
                    .info(new Info().title("测试 API")
                            .description("样例程序")
                            .version("v0.0.1")
                            .license(new License()
                                    .name("Apache 2.0我的协议")
                                    .url("http://springdoc.org")))
                    .externalDocs(new ExternalDocumentation()
                            .description("外部文档链接")
                            .url("https://springshop.wiki.github.org/docs"));
        }
    }
    

    WebFlux 中的文档生成方法参考下一篇《Spring WebFlux 简单业务代码及其Swagger文档

  • 相关阅读:
    对 Spring IoC 的理解
    初识 Spring 框架
    CSS 全局样式
    Bootstrap 12 栅格系统
    551 闭包,浏览器垃圾回收机制/内存收机制
    550 JavaScript运行机制之“堆栈”
    549 数据类型转换汇总:转换为Number、字符串、布尔,比 较操作==、===,练习题
    547 Promise:Ajax 的串行、并行, Promise的executor和状态,then、catch、finally,then链
    546 变量提升
    545 parseInt解析
  • 原文地址:https://www.cnblogs.com/somefuture/p/15471458.html
Copyright © 2011-2022 走看看