上一篇文章《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. JS制作日历小事件和数码时钟--JavaScript实例集锦(初学)

    运用JS的innerHTML,和for循环实现日历小部件内容和日期的转换. <!DOCTYPE html> <html> <head> <title>日 ...

  2. C#.Net筑基-运算符🔣Family

    C#运算符 内置了丰富的运算符操作类型,使用方便,极大的简化了编码,同时还支持多种运算符重载机制,让自定义的类型也能支持运算符行为. 01.运算符概览 运算符分类 描述 数学运算 基础的加减乘除,及+ ...

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

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

  4. 用 C 语言开发一门编程语言 — 基于 Lambda 表达式的函数设计

    目录 文章目录 目录 前文列表 函数 Lambda 表达式 函数设计 函数的存储 实现 Lambda 函数 函数的运行环境 函数调用 可变长的函数参数 源代码 前文列表 <用 C 语言开发一门编 ...

  5. flask注册功能

    一个项目的简单结构划分 首先创建一个新项目 可以正常运行与访问 创建配置文件并添加配置. 将这里拆分到不同的文件中,让启动文件更加简洁. 创建一个apps包,导入配置模块,导入Flask,定义创建ap ...

  6. 二叉树的遍历(BFS、DFS)

    二叉树的遍历(BFS.DFS) 本文分为以下部分: BFS(广度优先搜索) DFS(深度优先搜索) 先序遍历 中序遍历 后序遍历 总结 BFS(广度优先搜索) 广度优先搜索[^1](英语:Breadt ...

  7. Android项目代码规范

    项目代码规范 Android Studio的代码Style检查和inspect Code功能已经很强大,规范只负责代码结构和文件结构 带?的内容为可选或团队内协商内容 核心目标 提高可维护性: MVV ...

  8. zoxide更新后 (cd)异常

    关于zoxide github地址:https://github.com/ajeetdsouza/zoxide 简单来说 zoxide是一个cd的强化版.它会记录你曾经cd过的目录,在你使用cd的时候 ...

  9. Canny边缘检测实现(Opencv C++)

    Canny边缘检测是Canny在1986年提出来的,目前仍是图像边缘检测算法中最经典.先进的算法之一.canny方法基于如下三个基本目标: 1. 低错误率:所有边缘都应被找到,并且不应有虚假响应. 2 ...

  10. bash: _get_comp_words_by_ref: command not found 报错

    没有安装补全的包 错误信息 bash: _get_comp_words_by_ref: command not found 表明你的 shell 中可能存在补全功能的问题. 通常,这种错误发生在你的系 ...