基于OpenAPI、freemarker动态生成swagger文档

news2024/12/27 22:12:09

前言

spring项目中可以使用springfox或者springdoc,通过写注解的方式生成swagger文档,下面介绍一种不写注解,动态生成swagger文档的方式,在某些场景会适用,例如接口是动态生成的,此时swagger就不能通过注解来生成了。


一、定义swagger模板

通过观察一个swagger文档的openapi结构,将其中需要动态替换的部分写成变量,生成freemaker的ftl模板。
通过点击swagger图示链接可以查看openapi的json结构。
在这里插入图片描述
修改一个json结构,生成一个ftl模板,将模板放在springboot项目的resources/static/data-service-swagger-templates下面

{
  "openapi": "3.0.3",
  "info": {
    "title": "通用查询-[${interfaceName}]接口",
    "description": "通用查询接口",
    "version": "0.0.1"
  },
  "servers": [{
    "url": "${dataServicePrefix}",
    "description": "Generated server url"
  }],
  "security": [{
    "secretHeader": []
  }],
  "paths": {
    "${url}": {
      "post": {
        "tags": ["数据服务-通用查询接口"],
        "summary": "通用查询接口",
        "description": "通用查询接口,请求体采用统一数据结构",
        "operationId": "getData2UsingPOST",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DmoRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "*/*": {
                "schema": {
                  "$ref": "#/components/schemas/ResponseEntity"
                }
              }
            }
          },
          "201": {
            "description": "Created"
          },
          "401": {
            "description": "Unauthorized"
          },
          "403": {
            "description": "Forbidden"
          },
          "404": {
            "description": "Not Found"
          }
        }
      }
    }


  },
  "components": {
    "schemas": {
      "ResponseEntity": {
        "title": "ResponseEntity",
        "type": "object",
        "properties": {
          "desc": {
            "type": "string",
            "description": "错误详细描述"
          },
          "message": {
            "type": "string",
            "description": "如果为非200的返回,可以将此message提示给用户"
          },
          "requestURL": {
            "type": "string"
          },
          "stackTrace": {
            "type": "string",
            "description": "后端的异常栈信息,如果过长,只截取前面一部分"
          },
          "status": {
            "type": "integer",
            "description": "200:正常;401:未登陆;403:没有权限;400:请求参数校验失败;500:服务器内部错误",
            "format": "int32"
          },
          "tookInMillis": {
            "type": "integer",
            "description": "请求耗时",
            "format": "int64"
          },
          "value": {
            "type": "object"
          }
        }
      },
      "DmoRequest": {
        "title": "DmoRequest",
        "type": "object",
        "properties": {
          "fulltextNode": {
            "$ref": "#/components/schemas/QueryNode"
          },
          "node": {
            "$ref": "#/components/schemas/QueryNode"
          },
          "pageNumber": {
            "type": "integer",
            "description": "页码",
            "format": "int32"
          },
          "pageSize": {
            "type": "integer",
            "description": "每页条数",
            "format": "int32"
          },
          "showColumns": {
            "uniqueItems": true,
            "type": "array",
            "items": {
              "type": "string",
              "description": "显示的列"
            }
          },
          "sorts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DmoSort"
            }
          }
        }
      },
      "QueryNode": {
        "title": "QueryNode",
        "type": "object",
        "description": "查询条件",
        "properties": {
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/QueryNode"
            }
          },
          "data": {
            "$ref": "#/components/schemas/NodeData"
          },
          "type": {
            "type": "string",
            "description": "节点类型",
            "enum": ["AND", "LEAF", "OR", "ROOT"]
          }
        }
      },
      "NodeData": {
        "title": "NodeData",
        "type": "object",
        "description": "节点数据",
        "properties": {
          "operator": {
            "type": "string",
            "description": "操作符",
            "enum": ["BETWEEN", "EQ", "EXISTS", "GE", "GT", "IN", "IS_NOT_NULL", "IS_NULL", "LE", "LIKE", "LT", "NE", "NOT_BETWEEN", "NOT_EXISTS", "NOT_IN", "NOT_LIKE", "PREFIX", "REGEXP"]
          },
          "param": {
            "type": "string",
            "description": "参数名称,一般对应表的列名"
          },
          "value": {
            "type": "array",
            "description": "参数值",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "DmoSort": {
        "title": "DmoSort",
        "type": "object",
        "description": "排序",
        "properties": {
          "column": {
            "type": "string",
            "description": "列名"
          },
          "sortOrder": {
            "type": "string",
            "description": "排序方式",
            "enum": ["ASC", "DESC"]
          }
        }
      }
    },
    "securitySchemes": {
        "secretHeader": {
            "type": "apiKey",
            "name": "Authorization",
            "in": "header"
        }
    }
  }
}

二、使用freemarker生成openapi的JSON结构

1.引入库

代码如下(示例):

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>

2.生成json

下面的serviceInterface就是一个实体,可以自行定义


 @ApiOperation(value = "获取openapi的JSON", notes = "获取openapi的JSON")
    @GetMapping("/swagger-json/{id}")
    public String getSwaggerJson(@ApiParam(value = "id") @PathVariable Integer id) throws BaseException {
   		ServiceInterface serviceInterface = getServiceInterface(id);
        return getOpenApiJson(ServiceInterface serviceInterface, "test.ftl") ;
    }

 private String getOpenApiJson(ServiceInterface serviceInterface, String ftl) throws BaseException {
 		freemarker.template.Configuration configuration = new 		freemarker.template.Configuration(freemarker.template.Configuration.VERSION_2_3_0);
                    // 设置默认编码
                    configuration.setDefaultEncoding("utf-8");
                    //设置类加载器
                    configuration.setClassLoaderForTemplateLoading(this.getClass().getClassLoader(), "data-service-swagger-templates");
                    try {
                        // 生成模板对象
                        Template template = configuration.getTemplate(fileName);
                        TEMPLATE_CACHE.put(fileName, template);
                    } catch (Exception e) {
                        throw new BaseException(String.format("获取模版文件:[%s]出错", fileName), e);
                    }
        Template template = getFltTemplate(ftl);
        String dataServicePrefix = dataServiceProtocol + dataServiceUpstream;
        Map<String, String> dataMap = new HashMap<>();
        dataMap.put("interfaceName", serviceInterface.getServiceName());
        dataMap.put("dataServicePrefix", dataServicePrefix);
        dataMap.put("url", serviceInterface.getUrl());
        StringWriter sw = new StringWriter();
        try {
            template.process(dataMap, sw);
            return sw.toString();
        } catch (Exception e) {
            throw new BaseException("模板转换出错:" + e.getMessage(), e);
        }
    }

三、前端生成swagger示例

<!DOCTYPE html>
<html>
  <head>
    <title>数据服务接口文档</title>
    <link rel="stylesheet" type="text/css" href="swagger-ui.css"/>
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="swagger-ui-bundle.js"></script>
    <script>
      window.onload = function () {
        SwaggerUIBundle({
          // url: "http://localhost:14500/v3/api-docs", // 替换成您的OpenAPI规范的URL或文件路径
          //  url: "swagger-custom-select.json", // 替换成您的OpenAPI规范的URL或文件路径
          url: "http://192.168.33.22:3282/dmo/service-interface/swagger-json/226", // 替换成您的OpenAPI规范的URL或文件路径
          dom_id: "#swagger-ui",
          deepLinking: true,
        });
      };
    </script>
  </body>
</html>

其中url 为第二步的接口

用到的css和js下载地址:https://blog.csdn.net/weixin_41085315/article/details/124965953

四、测试

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1125486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用序列化技术保存数据 改进 IO流完成项目实战水果库存系统

上一节内容是 使用IO流完成项目实战水果库存系统https://blog.csdn.net/m0_65152767/article/details/133999972?spm1001.2014.3001.5501 package com.csdn.fruit.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java…

ZYNQ7020开发(二):zynq linux系统编译

文章目录 一、编译前准备二、SDK编译三、编译步骤总结四、问题汇总 一、编译前准备 1.设置环境变量 source /opt/pkg/petalinux/2020.2/settings.sh/opt/pkg/petalinux/2020.2是上一节petalinux的安装目录 2.创建 petalinux 工程 进入petalinux安装目录(例如&#xff1a;/op…

深度学习_6_实战_直线最优解_代码解析

问题描述&#xff1a; 上述题目的意思为&#xff0c;人工造出一些数据点&#xff0c;对我们的模型y Xw b ∈进行训练&#xff0c;其中标准模型如下&#xff1a; 其中W和X都为张量&#xff0c;我们训练的模型越接近题目给出的标准模型越好 训练过程如下&#xff1a; 人造数…

文件的基本操作(创建文件,删除文件,读写文件,打开文件,关闭文件)

1.创建文件(create系统调用) 1.进行Create系统调用时&#xff0c; 需要提供的几个主要参数: 1.所需的外存空间大小&#xff08;如:一个盘块&#xff0c;即1KB) 2&#xff0e;文件存放路径&#xff08;“D:/Demo”) 3.文件名&#xff08;这个地方默认为“新建文本文档.txt”) …

部署Vue项目到githubPage中

上传Vue项目到githubPage 例如: 看我发布的地址 前提条件 1. github上有一个仓库并且仓库下有两个分支(main 和 gh-pages) 1.1 main分支保存你的vue项目源码(react或者其他框架的都行) 1.2 gh-pages分支保存的是你项目打包之后的代码(如Vue项目打包完之后是个dist包,…

回归预测 | MATLAB实现IWOA-LSTM改进鲸鱼算法算法优化长短期记忆神经网络的数据回归预测(多指标,多图)

回归预测 | MATLAB实现IWOA-LSTM改进鲸鱼算法算法优化长短期记忆神经网络的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现IWOA-LSTM改进鲸鱼算法算法优化长短期记忆神经网络的数据回归预测&#xff08;多指标&#xff0c;多图&#…

会声会影2024旗舰版详细功能介绍

随着网络视频的蓬勃发展&#xff0c;越来越多的人开始涉足视频剪辑领域&#xff0c;毕竟技多不压身嘛。在众多剪辑软件中&#xff0c;剪映和会声会影是备受新手青睐的两种。那么&#xff0c;会声会影和剪映哪个好呢&#xff1f;在它们之间&#xff0c;哪一个更适合初学者呢接&a…

轻松修复缺少concrt140.dll,一键修复dll丢失问题

在电脑使用过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“由于找不到concrt140.dll无法继续执行代码”。这个问题可能是由于系统文件丢失、损坏或被病毒感染等原因引起的。为了解决这个问题&#xff0c;我整理了以下五个解决方案&#xff0c;希望能对…

如何能够获取到本行业的能力架构图去了解自己的能力缺陷与短板,从而能清晰的去弥补差距?

如何能够获取到本行业的能力架构图去了解自己的能力缺陷与短板&#xff0c;从而能清晰的去弥补差距&#xff1f; 获取并利用能力架构图&#xff08;Competency Model&#xff09;来了解自己在特定行业或职位中的能力缺陷和短板&#xff0c;并据此弥补差距&#xff0c;是一个非常…

YOLO V8训练自己的数据集并测试

目录 1 YOLOV8部署 2 标注软件labelme安装 3 将labelme转化为YOLOV8支持的数据格式 4 开始训练 5 利用训练结果进行测试 1 YOLOV8部署 我的一篇博客已经提到&#xff0c;这里不再赘述&#xff1a; YOLO V8语义分割模型部署-CSDN博客YOLO V8语义分割模型部署https://blog.cs…

DC-6 靶机

DC_6 信息搜集 存活检测 详细扫描 需要添加 DNS 解析 vim /etc/hosts后台网页扫描 dirsearch -u http://10.4.7.150网页信息搜集 使用 wappalyzer 插件识别 cms 指纹&#xff0c;发现为熟悉的 WordPress 并且发现了网站的登陆页面 直接上 wpscan 扫描用户 wpscan --url…

【Web】| CSS Float (浮动)的使用方法

Float&#xff08;浮动&#xff09;概念 CSS的Float&#xff08;浮动&#xff09;&#xff0c;会使得元素向左或者向右移动&#xff0c;其它周围元素也会重新排列。 Float浮动&#xff0c;往往是用于图像&#xff0c;但它的布局一样非常有效。 元素如何浮动 元素的水平方向…

23年上半年上午题复习

敏捷方法 耦合 软件维护 消息 面向对象测试 面向对象设计原则 包图 原型模式 数据库三级模型 数据库函数依赖 哈夫曼树 左0右1 折半查找 画一个折半查找树&#xff0c;这个树只会往一个方向查找&#xff0c;一个节点不会同时出现左右子树&#xff0c;较小的作为左子树&#…

redis 宕机恢复

1.集群现在状态 6个进程 主从分配如下 2. 关闭其中一个主节点 可以看到从节点转换成了主节点&#xff0c;7002主节点处在失败状态&#xff1a; 3.重新启动失败节点 可以看到启动后成为从节点&#xff1a; 另外&#xff0c;如果主节点宕机&#xff0c;从节点转换为主节点…

使用VisualSVN在Windows系统上设置SVN服务器,并结合内网穿透实现公网访问

文章目录 前言1. VisualSVN安装与配置2. VisualSVN Server管理界面配置3. 安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4. 固定公网地址访问 前言 SVN 是 subversion 的缩写&#xff0c;是一个开放源代码的版本控制系统…

【数据结构与算法】二叉树的运用要点

目录 一&#xff0c;二叉树的结构深入认识 二&#xff0c;二叉树的遍历 三&#xff0c;二叉树的基本运算 3-1&#xff0c;计算二叉树的大小 3-2&#xff0c;统计二叉树叶子结点个数 3-3&#xff0c;计算第k层的节点个数 3-4&#xff0c;查找指定值的结点 一&#xff0c;二叉…

循环神经网络(Recurrent Neural Network)

1. 为什么需要循环神经网络 RNN 上图是一幅全连接神经网络图&#xff0c;我们可以看到输入层-隐藏层-输出层&#xff0c;他们每一层之间是相互独立地&#xff0c;(框框里面代表同一层)&#xff0c;每一次输入生成一个节点&#xff0c;同一层中每个节点之间又相互独立的话&#…

【jvm】虚拟机栈之操作数栈

目录 一、说明二、图解2.1 代码示例2.2 javap操作 三、图示3.1 bipush 153.2 istore_13.3 bipush 83.4 istore_23.5 iload_13.6 iload_23.7 iadd3.8 istore_33.9 return结束 四、附加 一、说明 1.Operand Stack 2.栈可以使用数组或链表来实现 3.每一个独立的栈帧包含一个后进先…

Mac 开机提示Google LLC 注册 无法登录进入系统

Google LLC 会在电脑启动时提示如下弹窗&#xff0c;并要求登录谷歌账户进行验证 此时很明显没有用来进行验证的账号&#xff0c;所以需要关掉这个验证程序 从日志里面可以看到LLC启动了一个Tiny.app的程序 只需要想办法把这个程序删掉即可 关机 按住 Command R 开机 进入R…

【MySQL架构篇】MySQL字符集、大小写规范及默认数据库

文章目录 1. 字符集与字符集比较规则2. 大小写规范3. 默认数据库4. 与文件系统相关 1. 字符集与字符集比较规则 MySQL有4个级别的字符集和比较规则&#xff0c;分别是 服务器级别数据库级别表级别列级别 当创建对应表或列未指定字符集时&#xff0c;默认会取其上一级别的字符…