【总】前端 Swagger url 自动转 JavaScript方法(避免重复工作...)

前言

随着项目增多,且多数为项目定制化接口。避免前端重复封装,从 Swagger 入手,将 url 自动转 js 方法直接用。特别节约时间和资源~

Swagger 简介

Swagger 一款 RESTFUL 接口的、基于 YAML、JSON 语言的文档在线自动生成、代码自动生成的工具。

主要工具:

  • Swagger 编辑器 :基于浏览器的编辑器,可以在其中编写 OpenAPI 规范,并实时预览 API 文档。

  • Swagger UI :将 OpenAPI 规范呈现为交互式 API 文档,并可以在浏览器中尝试 API 调用。

  • Swagger Codegen :根据 OpenAPI 规范,生成服务器存根和客户端代码库。

实现

目录结构

bash 复制代码
.
├─ js
|   ├─MySwagger.js         # 主文件,1.url转js接口;2.接口查询界面解析接口;
|   └─ swagger             
|       ├─ axios.js	    # axios 对象及 包装后的 get,post,put,delete 等方法
|       └─ utils.js     # 工具文件,导入 axios.js,设置外部传入的 axios,url 解析为js方法名,定义 promise 等
└─ components
 	└─MySwaggerApi.vue # 组件,转换后的文件查看

主文件说明

MySwagger.js 中有两类方法

  • initSwaggerApi :用于生成 Js 接口;
  • initSwaggerDoc :用于界面显示接口信息;

文件 swagger/axios.js 和 swagger/utils.js 将在最后贴出

js 复制代码
import Utils from "./swagger/utils"
import axios from "axios"

// 请求法类型
const REQUEST_METHODS = [ 'get', 'post', 'put', 'delete', 'patch'];


export default class MySwagger {
    /**
     * 初始化 Swagger Javascript Api 接口
     * @param {Object} axios,axios对象,注意已带 token
     * @param {Object} cfg,swagger 相关信息 {ignorePaths, jsonUrl, ignorePaths}
     * @param {Function} callback 回调函数
     * @returns 
     */
    static initSwaggerApi(myaxios, cfg, callback) {
        // swagger/axios.js 文件中的方法,设置外部传入的 axios 对象
        Utils.setHttp(myaxios);
        // 解析 swagger json 对象
        axios.get(cfg.jsonUrl).then(res => {
            // paths 列表
            const paths = res.data.paths;

            for(const path in paths) {
                const methods = paths[path];

                for(const method in methods) {
                    if(!REQUEST_METHODS.includes(method)) {
                        continue;
                    }
                    // swagger/axios.js 文件中的方法,url 转换为 js 方法名
                    const apiMethodName = Utils.restUrlToMethodName(method, path, cfg.ignorePaths);

                    // 注意是 this,直接放在 类上,为静态方法。api 方法对象集合
                    this[apiMethodName] = function(odt, odt2, odt3) {
                        // swagger/axios.js 文件中的方法,定义 promise
                        return Utils.definePromise(path, method, odt, odt2, odt3)
                    };
                }
            }
            callback(true)
            console.log('Swagger API 自动转js方法成功!');
        }).catch(err => {
            callback(false)
            console.error('Swagger API 自动转js方法失败', err);
        })
    }
    
    /**
     * 初始化 Swagger Javascript Api 文档信息
     * @param {Object}   cfg swagger 相关信息 {jsonUrl, ignorePaths}
     * @param {Function} callback 成功回调 (data) 信息
     * @param {Function} errorback 失败回调 (e) 错误信息
     * @returns 
     */
    static initSwaggerDoc(cfg, callback, errorback) {
        let objs = {}
        // 解析 swagger json 对象
        axios.get(cfg.jsonUrl).then(res => {
            const data = res.data;
            // 判断 swagger 版本
            const version = (data.swagger || data.openapi).split('.')[0];
            // paths 列表
            const paths = data.paths;
			// swagger v2
            if(version==='2') { 
                for(const path in paths) {
                    const methods = paths[path];

                    for(const method in methods) {
                        // get/post 对象下
                        if(!REQUEST_METHODS.includes(method)) {
                            continue;
                        }
                        // swagger/axios.js 文件中的方法,url 转换为 js 方法名
                        const apiMethodName = Utils.restUrlToMethodName(method, path, cfg.ignorePaths);
                        const params = methods[method];
                        
                        const tag = params.tags[0];
                        if(!objs[tag]) {
                            objs[tag] = {
                                tag,
                                description: tag,
                                paths: []
                            }
                        }
                        objs[tag].paths.push({
                            path,
                            apiMethodName,
                            method,
                            summary: params.summary,// 接口描述
                            parameters: params.parameters,// 请求参数
                            version
                        })    
                    }
                }
            }
            else {// swagger v3,配置管理
                // 先解析 tag
                data.tags.forEach(t => {
                    objs[t.name] = {
                        tag: t.name,
                        description: `${t.name} ${t.description}`,
                        paths: []
                    }
                });
                // console.log('objs: ', objs);
                for(const path in paths) {
                    const methods = paths[path];

                    for(const method in methods) {
                        // get/post 对象下
                        if(!REQUEST_METHODS.includes(method)) {
                            continue;
                        }
                        // swagger/axios.js 文件中的方法,url 转换为 js 方法名
                        const apiMethodName = Utils.restUrlToMethodName(method, path, cfg.ignorePaths);
                        const params = methods[method];
                        
                        const tag = params.tags[0];
                        // console.log('objs[tag]', objs[tag], tag);
                        
                        // 要判断,有的 tags 与 path 不一致
                        objs[tag]?.paths.push({
                            path,
                            apiMethodName,
                            method,
                            summary: params.summary,// 接口描述
                            parameters: params.parameters,// 请求参数
                            version
                        })                
                    }
                }
            }
            callback(objs)
        }).catch(e => {
            errorback(e)
        })
    }    
}

功能使用

Swagger 解析配置

js 复制代码
const SWAGGER_CFG = {
    // 过滤不必要的路径
    ignorePaths: ['v1/', 'myweb/'],
    // swagger json 文件
    jsonUrl: 'http://127.0.0.1:8000/swagger/v1/swagger.json',
    // swagger 接口文件地址
    docUrl: 'http://127.0.0.1:8000/swagger/index.html'
}
生成 Js 接口
  • 代码第 13 - 20 行,创建 axios 对象,外部传入更灵活,可设置 token 等信息;
  • 代码第 13 - 20 行,初始化 js 接口到 MySwagger 类上;
vue 复制代码
<template>
    <div>
        <h1>swagger测试</h1>
    </div>
</template>

<script>
import axios from 'axios'
import MySwagger from '../js/index'
export default {
    mounted() {
        // 外部传入更灵活,可设置 token 等信息
        const myAxios = axios.create({
            // 基地址
            baseURL: `${window.location.protocol}//${IP}:${PORT}/`,
            // 请求头带 token
            headers:{ 
                'Authorization': '111ssss22s2s2s2s2s2s22s22' 
            }
        })

        // 初始化 js 接口到 MySwagger 类上
        MySwagger 对象上.initSwaggerApi(myAxios, SWAGGER_CFG, (data) => {
            console.log(`初始化成功:${data}`);
        }, (err) => {
            console.log(`初始化失败:${err}`);
       })

       window.MySwagger = MySwagger;
    }
}
</script>

为方便查看 MySwagger 挂在 window。但不推荐...控制台输出可看到接口都挂到类上

接口界面
  • 封装成组件 Vue <MySwaggerApi>
  • 代码第 4 行,可打开原始 Swagger 接口地址;
  • 代码第 78 - 98 行,可复制接口;
  • 代码第 104 - 111 行,调接口 MySwagger.initSwaggerDoc 获取列表;
vue 复制代码
<template>
    <div class="my-swagger-api__main">
        <div class="header">
            <el-link icon="el-icon-view" @click="openLink">Swagger 接口地址</el-link>
            <el-input v-model="searchKey" placeholder="路径搜索" @input="searchPath">
                <el-button icon="el-icon-search" ></el-button>
            </el-input>
        </div>
        <div class="main">
            <div class="row" v-for="item of tableData" :key="item.tag">
                <div class="first-title">{{item.description}}</div>
                <el-table class="api-list-table" :data="item.paths" border style="width: 100%" :row-class-name="tableRowClassName" @cell-click="onCopyText" >
                    <el-table-column type="expand" @click="copyHandle( 'icon-' + item.font_class)">
                        <template #default="props">
                            <el-form label-position="left" inline class="demo-table-expand">
                                <el-form-item label="请求方法:">
                                    <el-tag size="medium" :type="tagTypes[props.row.method]">
                                        {{ props.row.method }}
                                    </el-tag>
                                </el-form-item>
                                <el-form-item label="JS方法名:" >
                                    <el-tag size="mini" type="info">{{ props.row.apiMethodName }}</el-tag>
                                    <span></span>
                                </el-form-item>
                                <el-form-item label="描述:">
                                    <span>{{ props.row.summary }}</span>
                                </el-form-item>
                            </el-form>
                        </template>
                    </el-table-column>

                    <el-table-column prop="path" label="路径"> </el-table-column>
                    <el-table-column prop="apiMethodName" label="JS方法名" class-name="api-method-name-cell"> </el-table-column>
                    <el-table-column prop="summary" label="描述"> </el-table-column>
                </el-table>
            </div>
        </div>
    </div>
</template>

<script>
import MySwagger from '../js/MySwagger'
export default {
    props: {
        config: {
            type: Object,
            default() {
                return {
                    // ignorePaths: ['v1/'],
                    // jsonUrl: 'http://127.0.0.1:7008/swagger/v1/swagger.json',// 解析的 json 路径
                    // docUrl: 'http://127.0.0.1:7008/swagger/index.html' // 官方接口列表
                }
            }
        }
    },
    data() {
        return {
            tableData: {},//API_LIST,
            tagTypes: {
                post: 'success',
                get: '',
                put:'info',
                delete: 'danger',
                patch: 'warning'
            },
            searchKey: '',
        }
    },
    methods: {
        tableRowClassName({ row, rowIndex }) {
            // return `${row.method}-row`;
        },
        // 打开
        openLink() {
            window.open(this.config.docUrl, '_blank')
        },
        // 拷贝
        onCopyText(row, column, cell, event) {
            console.log(row, column, cell, event);
            if(column.property!=='apiMethodName') {
                return
            }
            // 获取输入框的内容
            const copyText = row.apiMethodName; 
            
            let copy = (e) => {
                e.preventDefault()
                e.clipboardData.setData('text/plain', copyText)
                // 提示用户复制成功
                this.$message({
                    type: 'success',
                    message: `${column.label} 复制成功`
                })
                document.removeEventListener('copy', copy)
            }
            document.addEventListener('copy', copy)
            document.execCommand('copy')
        },
        // 查询
        searchPath(val) {
            console.log(this.searchKey, val);
        }
    },
    mounted() {
        // 初始化
        MySwagger.initSwaggerDoc(this.config, (data=>{
            this.tableData = data;
            console.log('this.tableData',this.tableData);
        }),(e)=>{
            console.error('获取错误',e);
        });
    },
};
</script>

<style lang="scss" scoped>
.my-swagger-api__main {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    overflow: hidden;
    .header {
        padding: 50px 50px 20px 50px;
        display: flex;
        justify-content: space-between;
        width: 100%;
        .el-input {
            width: 350px;    
        }    
    }
}
.main {
    position: absolute;
    top: 100px;
    left: 50px;
    right: 50px;
    bottom: 50px;
    overflow: auto;
    .row {
        margin-bottom: 20px;
    }
}
.first-title {
    font-size: 17px;
    font-weight: 600;
    height: 45px;
    line-height: 45px;
    padding-left: 5px;
    border-left: 4px solid #0d64d4;
    color: #333;
    background:#f0f8ff;
}
.demo-table-expand {
    margin-left: 50px;
    background:#fafdff;

    :deep(.el-form-item) {
        display: block;
        .el-form-item__label {
            width: 90px;
        }
    }
}
.api-list-table {
    :deep(.api-method-name-cell) {
        cursor: pointer;
    }
}

</style>
<style lang="scss">

.get-row {
    background: rgba(97, 175, 254, .5) !important;
}
.post-row {
    background: rgba(73, 204, 144, .5) !important;
}
</style>

界面组件的使用

vue 复制代码
<template>
    <my-swagger-api :config="config"/>
</template>

<script>
import MySwaggerApi from '../components/MySwaggerApi.vue';
export default {
    components: {
        MySwaggerApi
    },
    data() {
        return {
            config: SWAGGER_CFG
        }
    }
}
</script>

效果

使用

此处只列出特殊的。

Post

{urlParams}
  • Post 方式,同时存在 Parameters 和 RequestBody
  • 第一个参数:对象类型,RequestBody。如果没参数,就传 null;
  • 第二个参数,对象类型,Parameters。注意 key 与下图一致;
js 复制代码
// 第一个参数 RequestBody
const requestBody = { 
    ip: '127.0.0.1',
    port: '6163'
};

// 第二个参数 Parameters
const parameters = {
    dataSourceName: 'pg123'
};
MySwagger.postConfSpatDatasourceEdit(requestBody, parameters);
url + Params
复制代码
/v1/gms/conf/set-default-slice-cache-db
  • Post 方式,同时存在 Parameters 和 RequestBody
  • 第一个参数:对象类型,RequestBody。如果没参数,就传 null;
  • 第二个参数,对象类型,Parameters。注意 key 与下图一致;
js 复制代码
// 第二个参数 Parameters
const parameters = {
    id: 'pg123'
};
MySwagger.postConfSetDefaultSliceCacheDb(null, parameters).then(res=>{
   console.log(res);             
}).catch(err => {
	console.log(err);
})

Get

{urlParams}
  • Get 方式,在 URL 上赋值参数
  • 第一个参数,对象类型,Parameters。注意 key 与下图一致;
js 复制代码
// 第一个参数是对象类型,且 key 与下图一致
const parameters = {
    dataSourceName: 'pg123'
}

// 请求
MySwagger.getConfSpatDatasourceInfo(parameters).then(res=>{
    console.log(res);
}).catch(err => {
    console.error(err);
})
url + Params
复制代码
/v1/gms/conf/spat/gdb-list
  • Get 方式,在 URL 上赋值参数
  • 第一个参数,对象类型,Parameters。注意 key 与下图一致;
js 复制代码
// 第一个参数是对象类型,且 key 与下图一致
const parameters = {
    gdbp: 'pg123'
}

MySwagger.getConfSpatGdbList(parameters).then(res=>{
    console.log(res);
}).catch(err => {
    console.log(err);
})

最后

swagger/axios.js

js 复制代码
// 导入封装好的axios实例
let service;

export default {
    
    // axios实例,外部传入,用于外部设置 token
    setHttp(axios) {
        service = axios;
    },

    /**
     * get 请求
     * @param {String} url     必填,请求地址 
     * @param {Object} params  选填,与请求一起发送的 url 参数
     * @param {Object} options 选填,其它配置参数
     */
    get(url, params, options = {}) {
        // 是否解析带参数的url
        url = parseParamsUrl(url, params);
        // 参数
        const config = {
            method: 'get',
            url: encodeURI(url)
        }
        // 是否有 params
        if (params) config.params = params
        // 其它参数
        for (let key in options) {
            config[key] = options[key]
        }
        // console.log('get',config);
        return service(config)
    },
    /**
     * post 请求
     * @param {String} url     必填,请求地址 
     * @param {Object} data    选填,请求主体数据
     * @param {Object} params  选填,与请求一起发送的 url 参数
     * @param {Object} options 选填,其它配置参数
     */
    post(url, data, params, options = {}) {
        // 是否解析带参数的url
        url = parseParamsUrl(url, params);
        // 参数
        const config = {
            method: 'post',
            url: encodeURI(url)
        }
        // 是否有 params
        if (params) config.params = params
        // 是否有 data
        if (data) config.data = data
        // 其它参数
        for (let key in options) {
            config[key] = options[key]
        }

        // console.log('post',config);
        return service(config)
    },
    put(url, params) {
        const config = {
            method: 'put',
            url: encodeURI(url)
        }
        if (params) config.params = params
        return service(config)
    },
    delete(url, params) {
        const config = {
            method: 'delete',
            url: encodeURI(url)
        }
        if (params) config.params = params
        return service(config)
    }
}


/**
 * 解析 url 带参数
 * @param {String} url     必填,请求地址 
 * @param {Object} params  选填,与请求一起发送的 url 参数
 */
function parseParamsUrl(url, params) {
    if(params && Object.keys(params) && Object.keys(params).length > 0) {
        Object.keys(params).forEach(key => {
            // 对应的参数
            const reg = `{${key}}`;
            if(url.indexOf(reg) > -1) {
                url = url.replace(reg, params[key])
                
            }
        })
    }
    // console.log('url2', url);
    return url;
}

swagger/utils.js

js 复制代码
import http from "./axios.js"

export default {
    
    // axios实例,外部传入,用于外部设置 token
    setHttp(axios) {
        http.setHttp(axios)
    },

    /**
     * url 转方法名
     * @param {*} method:get, post, put, delete, patch
     * @param {*} url
     * @returns 
     */
    restUrlToMethodName(method, url, ignorePaths) { 
        // 把 -或者 . 也换成 /
        url = url.replace(/-|\./g, '/');
        // 去除不要的
        ignorePaths.forEach(p=>{
            url = url.replace(p, '');
        })
        // 移除路径中的斜杠和参数占位符  
        const paths = url.split('/');
        const methodName = paths.reduce((x,y)=>{
            // 去掉 {},首字符大写
            const part = y.replace(/\{.*?\}/g, '');
            // 首字符大写
            const part2 = part.charAt(0).toUpperCase() + part.slice(1);
            return x + part2;
        }, '') 
        
        return method + methodName;  
    },
    /**
     * 定义 promise
     * @param {String} url
     * @param {String} method:get, post, put, delete, patch
     * @param {Object} odt,如果方法是 get,此参数为 params。如果是 post,为 RequestBody
     * @param {Object} odt2,如果方法是 get,此参数为 options。如果是 post,为 params
     * @param {Object} odt3,如果方法是 post,为 params
     * @returns 
     */
    definePromise(url, method, odt, odt2, odt3) {
        let promise;
        switch(method) {
            case 'get': {
                promise = new Promise(function(resolve, reject) {
                    http.get(url, odt, odt2).then(function(resp) {
                        const d = resp.data;
                        (d && d.succ) ? resolve(d.obj||d): reject(d);
                    }).catch(e=>{
                        reject(e)
                    })
                })
                break;
            }
            case 'post': {
                promise = new Promise(function(resolve, reject) {
                    http.post(url, odt, odt2, odt3).then(function(resp) {
                        const d = resp.data;
                        (d && d.succ) ? resolve(d): reject(d);
                    })
                })
                break;
            }
        }
        return promise;
    },



}
相关推荐
徐子颐10 分钟前
从 Vibe Coding 到 Agent Coding:Cursor 2.0 开启下一代 AI 开发范式
前端
开心-开心急了13 分钟前
pyside6实现win10自动切换主题
开发语言·python·pyqt·pyside
小月鸭22 分钟前
如何理解HTML语义化
前端·html
沐知全栈开发37 分钟前
Foundation 模态框
开发语言
jump6801 小时前
url输入到网页展示会发生什么?
前端
wjs20241 小时前
CSS 导航栏
开发语言
诸葛韩信1 小时前
我们需要了解的Web Workers
前端
brzhang1 小时前
我觉得可以试试 TOON —— 一个为 LLM 而生的极致压缩数据格式
前端·后端·架构
共享家95271 小时前
Qt窗口教程(上)
开发语言·qt
yivifu1 小时前
JavaScript Selection API详解
java·前端·javascript