乐趣区

关于api:教你如何开发一个导入导出插件

在开发过程中,或多或少都会遇到数据格式转换的问题,如果只是简略的数据,那天然用什么形式都能够,如果遇到数据十分多、层级简单、关联性强的数据,则须要摸索出一套适合的,本文会介绍两种比拟可行的转换模型,他们各自适宜不同的场景和爱好。

在不同的平台上开发导入 / 导出插件,天然会受到该平台硬性条件的束缚,例如供主零碎调用的函数名、约定的传入和返回的参数模式、与主零碎共享的环境变量等等。这些方面各自的平台难免会有所差别,难以对立探讨,但其本质是数据结构的转换,也是本文想要探讨的主题。

例如当初咱们有一份本人零碎所属的接口数据,须要将其转换成 postman 平台的数据。咱们依照难度,由浅及深地探讨如何解决各种场景。

在开始之前,咱们先约定一下术语,无论是导入还是导出,咱们将转换前的数据称之为源数据,将转换后的数据,称之为指标数据。无论在性能上是导入或导出,咱们所做的事件是相似的,都是将一种数据结构转换成另一种数据结构。

字段差别
最简略的状况,字段内容一样,仅仅是字段名不一样,例如在咱们的数据中,接口申请地址叫做 url , 在指标数据格式中,申请接口地址叫做 uri,那只须要做一下简略的转换即可:

const translate = ({url}) => {
    return {uri: url}
}

们将难度略微晋升一点,思考更简单一些的状况,是字段之间的关系是一对多,或多对一。例如在源数据中,须要用 host + path 两个字段来形容接口地址,而在指标数据中仅用一个字段 url 来形容。在这种状况下,咱们最好应用另一个小函数来解决:

const translatePath = ({host, path}) => ({url: host + path})

const translate = ({host, path}) => {
    return {...translatePath({ host, path})
    }
}

在一对多、或多对一、多对多这几种字段关系中,都能够用该思路,灵便地封装出 translateRequesttranslateResponse 等多个函数,将他们在返回值中重叠在一起:

const translate = ({host, path, request, response}) => {
    return {...translatePath({ host, path}),
        ...translateRequest({path, request}),
        ...translateResponse({response})
    }
}

大抵的解决模型如此,因为每一个子函数都是独立运行、独立解析,不存在缓存和副作用,因此较好保护,在传参、取参时能够灵便管制本人须要或不要的参数;一些非空解决、边界解决,也都能够在对应的子函数中操作;庆幸 ES6+ 提供了比拟优雅的解构语法,能让程序整体看起来较为简洁。

层级差别

下面所说的都是字段与字段之间的关系,但数据结构之间的差别不只是有字段,还有层级。如果沿用下面的解决模式,遇到层级较深、并且含有级联关系的数据结构,可能会难于解决。因而,在这里须要介绍另一种解决模型——基于 class 语法的模型。

为什么须要引入class

并非是要对数据结构进行形象,而是基于 class 比拟容易应用链式调用语法,如有须要,咱们还能够比拟不便地应用缓存。

还是以接口数据为例,咱们思考这样一种场景,在源数据中,接口的 rest 参数,定义在最外层,而在指标数据中,rest 参数定义在 request 字段中,两者之间差了一个层级。我心愿以这样的语法解决数据结构间的转换:
new Translate(source).translateRest()
同理,也是一个函数只做一件事,只不过在语法上,各个子函数以链式调用的模式联合在一起:

new Translate(source)
    .translateRest()
    .translateRequest()
    .translateResponse()

那么 Translate 这个类须要怎么实现呢?

其实不难,只有咱们捡起远古的记忆,回到最后的 jQuery,在每个子函数解决后,返回this 即可。至于每个子函数转换的数据,天然是存在类外部的公有属性中,也能够了解为缓存。因而在最初,咱们须要一个额定的函数,帮忙咱们将曾经转换好的缓存数据取出来。

new Translate(source)
    .translateRest()
    ...
    .translateResponse()
    .getResult()

Translate类中大抵须要这么几个部件:

  • 至多须要两个缓存变量,这里临时称之为 sourceresult,前者用于寄存源数据,后者用于寄存转换中的数据。
  • constructor 中,将承受到的源数据缓存起来,不便前面的子函数取用。
  • 各个子函数每次都从缓存中取出源数据,依据源数据的数据特色,从特定的层级中取出特定的字段,进行转换,而后塞入到 result 对应的层级中,随后记得返回 this。
  • 在转换完结后,调用 getResult 办法,将 result 取出,在这一步就不再返回 this 了。
  • 最初一个,依据我本人的教训,还须要有一个不便打印数据的函数,暂且叫 log 吧。
    咱们即刻实现一下基础架构,除了 Class 的语法外,还能够联合前半段介绍的解决模型来用:
const translateRest = ({rest}) => ({...    // 返回解决好的格局})

class Translate {constructor(source) {
        // 初始化
        this.source = source
        this.result = {}}
    translateRest() {
        // 在该函数中,比拟好地解决了层级问题
        this.result.request = {...translateRest(source)
        }
        return this
    }
    translateResponse() {
        this.result.response = {...}
        return this
    }
    log() {
        // 格式化输入
        console.log(JSON.stringify(this.result, null, 2))
        return this
    }
    getResult() {
        // 这是链式解决的最初一步
        return this.result
    }
}

这样的解决模型,的确是毁坏了子函数的原子性,因为它不得不对外部的缓存进行读写,引入了一些副作用,但根本的缓存只有两个,简单不会太高。然而用这个模型,能让解决层级差别变得简略,你能够用多个子函数解决某一层级中的多个字段(可能要留神先后顺序),比方下个例子就用三个子函数,来解决 request 层级下的多个字段:

new Translate(source)
    .translateRequestHeader()
    .translateRequestBody()
    .translateRequestQuery()
    ...

总结起来,该模型有以下几个显著的劣势:

  1. 得益于缓存,子函数能够任意读取源数据中多个层级的字段。同理地,子函数也能够在任意一个层级写入指标数据。
  2. 因为是链式语法,在解决链路中,能够比拟灵便地插入子函数、调整程序,比方在任意一个子函数前面插入 log 函数进行打印,这一特点在开发和调试中尤其不便。
new Translate(source)
    .translateRest()
    ...
    .log()    // 随时插入打印
    .translateResponse()
    .getResult()

须要留神是,因为应用了缓存,因而在开发过程中,须要留神深浅拷贝的问题。

结尾

本文大抵由浅及深地介绍了两种解决模型,相应可能应酬大部分数据结构转译的场景。当实现了整个算法后,接下来要做的事件就只是依照平台的插件规定,将其包装成一个合格的插件。
咱们就是基于这样的独立模型,在开源产品 Eoapi 产品中实现 OpenApi 格局的导入和导出插件。当然,OpenApi 的数据结构比较复杂,咱们目前只是开个了头,正在逐步完善。Eoapi 的插件仓库
感激浏览本文,心愿你从此也可能轻松解决数据结构的转换。

Eoapi 是一款类 Postman 的 开源 API 工具 ,它更轻量,同时可拓展。
我的项目地址: https://github.com/eolinker/e…
文档地址: https://docs.eoapi.io/?utm_so…
官网地址: https://www.eoapi.io/

如果你对于 Eoapi 有任何疑难或者倡议,都能够去 Github 或者来这里找我,提个 Issue,我看到了都会及时回复的。

退出移动版