上一篇文章《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. 深度学习框架火焰图pprof和CUDA Nsys配置指南

    注:如下是在做深度学习框架开发时,用到的火焰图pprof和 CUDA Nsys 配置指南,可能对大家有一些帮助,就此分享.一些是基于飞桨的Docker镜像配置的. 一.环境 & 工具配置 0. ...

  2. Python基础知识——缩进、标识符、保留字

    标识符 标识符就是程序中,使用的各种名称,例如:变量名.常量名.类名等等. 在 Python 中,对标识符格式的要求与 C/C++.Java 等差不多: 第一个字符必须是字母表中的字母或下划线 _ ; ...

  3. Docker推送镜像到Dockerhub

    登录docker hub官网注册账号 https://hub.docker.com/signup 登录账户,创建一个仓库 "Create Repository"--> 输入命 ...

  4. 2023最新nacos的windows 10安装(保姆级)

    目录 前景提要 一.环境整合 构建工具(参考工具部署方式) 二.找到下载位置 1.在百度中找到下载地址.搜索nacos中文官网. 2.双击进入这个网站,看到首页找到下载位置. 3.进入git地址找到如 ...

  5. 我发现了字节OpenApi接口的bug!

    本文记录我在对接字节旗下产品火山云旗下云游戏产品 OpenApi 接口文档时遇到的坑,希望能帮助大家(火山云旗下云游戏产品的文档坑很多,我算是从零到一都踩了一遍,特此记录,希望大家引以为鉴). 1. ...

  6. 『手撕Vue-CLI』拉取模板名称

    前言 好,经过上篇文章的介绍,已经可以有处理不同指令的能力了,接下来我们就来处理 vue create 指令,这个指令的本质就是从网络上下载提前准备好的模板,然后再自动安装模板中相关依赖. 所以实现 ...

  7. Redis 的简单介绍

    Redis 特点 单线程 执行过程按顺序执行,不会同时执行多个操作,保证操作的原子性,省去了很多上下文切换线程的时间,不必考虑资源竞争和可能出现死锁. 为什么使用单线程 ? 官方FAQ表示:因为 Re ...

  8. 8.17考试总结(NOIP模拟42)[卷·简单题·粉丝·字符串]

    你的败因只有一个,就是与我为敌. ­ T1 卷 解题思路 乍一看,简单的树形 DP . 后来一看数据范围,发现事实并非如此.(\((10^9)^{2\times 10^5}\)????) 毕竟取 \( ...

  9. uniapp 拨打电话功能

    phoneNumber进行动态调用时候一定要添加引号,否则会报错 1 call() { 2 uni.makePhoneCall({ 3 phoneNumber: 'this.leads.tel' // ...

  10. 面试必会->Redis篇

    01- 你们项目中哪里用到了Redis ? 在我们的项目中很多地方都用到了Redis , Redis在我们的项目中主要有三个作用 : 使用Redis做热点数据缓存/接口数据缓存 使用Redis存储一些 ...