【总】前端 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;
    },



}
相关推荐
Swift社区1 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
new出一个对象1 小时前
uniapp接入BMapGL百度地图
javascript·百度·uni-app
没头脑的ht1 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht1 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20241 小时前
Swift 数组
开发语言
你挚爱的强哥2 小时前
✅✅✅【Vue.js】sd.js基于jQuery Ajax最新原生完整版for凯哥API版本
javascript·vue.js·jquery
stm 学习ing2 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
y先森2 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy2 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189112 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js