上一篇文章《Spring 5 中函数式webmvc开发中的swagger文档》中讲了如何给传统MVC开发模式中的RouterFunction增加swagger文档。这一篇讲一下如何给函数式WebFlux开发增加Swagger文档。

类似于MVC的webflux开发(基于Controller实现)的swagger和web mvc方法一样,这里不讲了

依赖变更

既然要从webmvc改造成webflux,就需要换一下依赖。

首先需要增加webflux依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

接下来引入对应的doc依赖:

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

业务代码改造

我们首先把传统web中的代码改造成webFlux的。之前的Handler中的方法返回的都是org.springframework.web.reactive.function.server.ServerResponse

public ServerResponse getStations(ServerRequest req) {
Long entId = Long.valueOf(path(req, "entId"));
List<StationBO> stationBoList = modelBuildingService.getStations(entId);
List<StationVO> stationVoList = TransformUtils.transformList(stationBoList, StationVO.class);
return body(PagedResult.success(stationVoList));
} public ServerResponse getDeviceTypes(ServerRequest req) {
String stationId = path(req, "stationId");
List<DeviceTypeBO> deviceTypeBoList = modelBuildingService.getDeviceTypes(stationId);
List<DeviceTypeVO> deviceTypeVoList = TransformUtils.transformList(deviceTypeBoList, DeviceTypeVO.class);
return body(PagedResult.success(deviceTypeVoList));
} // 其他方法

其中path()body()定义在基类中:

public abstract class BaseHandler {
protected ServerResponse body(Object body) {
return ServerResponse.ok().body(body);
} protected String path(ServerRequest req, String path) {
return req.pathVariable(path);
} protected <T> T req(ServerRequest req, Class<T> clazz) {
try {
return req.body(clazz);
} catch (ServletException | IOException e) {
throw new RuntimeException(e);
}
}
}

Reactor中有两个实体,一个是Mono,表示单个对象,可以为空;一个是Flux,表示对象的集合(以序列的形式)。我们这里的接口都是返回的集合,所以应该改造成Flux。但是一来为了方便,二来我们的数据是一次性拿到,过程并不是响应式的,所以也无需用Flux,所以我们改造成Mono:

public Mono<ServerResponse> getStations(ServerRequest req) {
Long entId = Long.valueOf(path(req, "entId"));
List<StationBO> stationBoList = modelBuildingService.getStations(entId);
List<StationVO> stationVoList = TransformUtils.transformList(stationBoList, StationVO.class);
return body(PagedResult.success(stationVoList));
} public Mono<ServerResponse> getDeviceTypes(ServerRequest req) {
String stationId = path(req, "stationId");
List<DeviceTypeBO> deviceTypeBoList = modelBuildingService.getDeviceTypes(stationId);
List<DeviceTypeVO> deviceTypeVoList = TransformUtils.transformList(deviceTypeBoList, DeviceTypeVO.class);
return body(PagedResult.success(deviceTypeVoList));
} public Mono<ServerResponse> getRealTimeData(ServerRequest req) {
return req(req, RealTimeDataQueryVO.class)
.doOnNext(body -> {
log.info("请求参数{}", body);
})
.flatMap(body -> {
RealTimeDataResultBO resultBo = modelBuildingService.getRealTimeData(transferRealTimeDataQueryParam(body));
return body(Result.success(transferRealTimeDataResult(resultBo)));
});
}

可以看到,我们获取数据的方式不是响应式的,这样实际上并不能发挥出webFlux的能力。另外看到,Mono对象的生成都是通过基类来的,所以基类改造成了:

public abstract class BaseHandler {
protected Mono<ServerResponse> body(Object body) {
return ServerResponse.ok().bodyValue(body);
} protected String path(ServerRequest req, String path) {
return req.pathVariable(path);
} protected <T> Mono<T> req(ServerRequest req, Class<T> clazz) {
return req.bodyToMono(clazz);
}
}

这里用到了一些WebFlux的API,感兴趣的可以独自去了解,这里就先不介绍了。

RouterFunction的代码几乎不用改变,只要把引入的包从org.springframework.web.servlet.function改到org.springframework.web.reactive.function.server即可,之前的swagger就依然生效:

import com.enn.view.studio.web.handler.ModelBuildingHandler;
import com.enn.view.studio.web.vo.request.RealTimeDataQueryVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springdoc.core.annotations.RouterOperation;
import org.springdoc.core.annotations.RouterOperations;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse; import static org.springframework.web.reactive.function.server.RequestPredicates.*; @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"
)
)
),
@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)
)
)
)
)
})
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)
);
} }

问题排查

如果增加了上面这个依赖,但是swagger页面打不开,或者swagger页面看不到RouterFunction的文档(只能看到Controller的文档),通常都是因为依赖冲突。我本地的老项目引入上述依赖后调整了好几天依赖才成功显示出文档来。

  • 不要依赖springdoc-openapi-ui

    依赖了springdoc-openapi-webflux-ui就把springdoc-openapi-ui移除掉
<!--<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>-->
  • 不要依赖springdoc-openapi-webmvc-core

    即使同时使用了mvc和webflux,也不要再依赖springdoc-openapi-webmvc-core
<!--<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-webmvc-core</artifactId>
<version>1.5.12</version>
</dependency>-->
  • 不要依赖spring-boot-starter-web

    这个可能因项目而异。我这边需要删掉这个依赖才能启动
<!--<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>-->

Spring WebFlux 简单业务代码及其Swagger文档的更多相关文章

  1. Spring Boot:整合Swagger文档

    综合概述 spring-boot作为当前最为流行的Java web开发脚手架,越来越多的开发者选择用其来构建企业级的RESTFul API接口.这些接口不但会服务于传统的web端(b/s),也会服务于 ...

  2. 如何Spring Cloud Zuul作为网关的分布式系统中整合Swagger文档在同一个页面上

    本文不涉及技术,只是单纯的一个小技巧. 阅读本文前,你需要对spring-cloud-zuul.spring-cloud-eureka.以及swagger的配置和使用有所了解. 如果你的系统也是用zu ...

  3. Spring Boot中使用Swagger2构建API文档

    程序员都很希望别人能写技术文档,自己却很不愿意写文档.因为接口数量繁多,并且充满业务细节,写文档需要花大量的时间去处理格式排版,代码修改后还需要同步修改文档,经常因为项目时间紧等原因导致文档滞后于代码 ...

  4. Swagger文档转Word 文档

    GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer 原创作品,转载请注明出处:http://www.cnblogs.co ...

  5. Spring Boot 项目学习 (四) Spring Boot整合Swagger2自动生成API文档

    0 引言 在做服务端开发的时候,难免会涉及到API 接口文档的编写,可以经历过手写API 文档的过程,就会发现,一个自动生成API文档可以提高多少的效率. 以下列举几个手写API 文档的痛点: 文档需 ...

  6. Swagger文档转Word

    Swagger文档转Word 文档   GitHub 地址:https://github.com/JMCuixy/SwaggerToWord/tree/developer 原创作品,转载请注明出处:h ...

  7. springboot成神之——swagger文档自动生成工具

    本文讲解如何在spring-boot中使用swagger文档自动生成工具 目录结构 说明 依赖 SwaggerConfig 开启api界面 JSR 303注释信息 Swagger核心注释 User T ...

  8. Core + Vue 后台管理基础框架8——Swagger文档

    1.前言 作为前后端分离的项目,或者说但凡涉及到对外服务的后端,一个自描述,跟代码实时同步的文档是极其重要的.说到这儿,想起了几年前在XX速运,每天写完代码,还要给APP团队更新文档的惨痛经历.给人家 ...

  9. 懒得写文档,swagger文档导出来不香吗

    导航 前言 离线文档 1 保存为html 2 导出成pdf文档 3 导出成Word文档 参考 前言   早前笔者曾经写过一篇文章<研发团队,请管好你的API文档>.团队协作中,开发文档的重 ...

  10. 手动编写Swagger文档与部署指南

    Swagger介绍 在Web开发中,后端开发者在完成接口开发后,需要给前端相应的接口使用说明,所以一般会写一份API文档.一般来说,有两种方式提供API接口文档,一种是利用插件在代码中自动生成,另一种 ...

随机推荐

  1. iceoryx源码阅读(八)——IPC通信机制

    目录 1 整体结构 2 序列化与反序列化 3 类Unix系统的实现 3.1 发送函数send 3.2 接收函数receive 4 Windows系统的实现 4.1 发送函数send 4.2 接收函数r ...

  2. vue中render函数和h函数

    "render"函数是Vue组件的一个重要方法,它用于描述组件的视图结构,并负责渲染虚拟DOM树."render"函数是一个JavaScript函数,它接受一个 ...

  3. 微信小程序长按识别二维码

    微信小程序长按识别二维码 image 组件中二维码/小程序码图片不支持长按识别.仅在 wx.previewImage 中支持长按识别示例代码

  4. [kernel] 带着问题看源码 —— 进程 ID 是如何分配的

    前言 在<[apue] 进程控制那些事儿>一文中,曾提到进程 ID 并不是唯一的,在整个系统运行期间一个进程 ID 可能会出现好多次. > ./pid fork and exec c ...

  5. pageoffice6 实现提取数据区域为子文件(Word拆分)

    在实际的开发过程中,有时会遇到希望提取Word文档中部分内容保存为子文件的需求,PageOffice支持提取Word文档数据区域中的内容为一个Word文件流,在服务器端创建PageOffice的Wor ...

  6. 降本提效 | AIRIOT设备运维管理解决方案

      传统运维多是使用在本地化系统,以人工运维和独立系统执行运维工作,重点关注的是设施运行,存在以下几个问题:   1.信息孤岛:本地化系统的接口不同,功能单一独立,各个系统之间的数据无法对接.交互,形 ...

  7. java 执行 javascript 代码

    package com.ruoyi.project.front.controller; import java.math.BigDecimal; import java.util.*; import ...

  8. linux使用过程中遇到的常见问题

    1 xxxx is not in the sudoers file. This incident will be reported. 解决方式:https://www.cnblogs.com/xym4 ...

  9. Redis 常用的数据结构简介与实例测试【Redis 系列二】

    〇.都有哪些数据结构? Redis 提供了较为丰富的数据类型,常见的有五种:String(字符串),Hash(哈希),List(列表),Set(集合).Zset(有序集合). 随着 Redis 版本的 ...

  10. kettle从入门到精通 第五十三课 ETL之kettle MQTT/RabbitMQ producer 实战

    1.MQTT介绍 MQTT (Message Queuing Telemetry Transport) 是一种轻量级的消息传输协议,设计用于连接低带宽.高延迟或不可靠网络的设备. MQTT 是基于发布 ...