We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文原创,著作权归WGrape所有,未经授权,严禁转载
在《关于接口文档高效治理方案的研究和思考》一文中已经详细介绍了高效管理接口文档的重要性,更多内容可以阅读原文。这里就不再解释为什么需要,而是重点关注怎么做。
在本文接下来的内容中,会为大家分享一种轻量级的创新型方案,它不但可以简单高效实现接口文档的自动生成,而且可以满足前端人员Mock接口的需要。这就是基于数据Mock的接口治理方案,请慢慢往下看,大约会花费8分钟的时间。
接口治理的第一个目标就是实现自动生成接口文档。后端人员只需在开发接口的同时按照规范编写相应逻辑,即可在CI阶段自动生成接口文档。
接口治理的第二个目标就是满足前端人员Mock接口的需要。无论是开发、联调、测试、上线后的哪一个阶段,前端都可以根据自己的需要,随时Mock后端的接口。
我们知道,无论是什么类型的接口文档,其必须具备以下组成元素
不过有些信息比如参数is_new表示是否为新用户,这些信息都是属于程序无法自动获取的自定义内容,它们还是需要人力提供的,只是提供的方式可能有所不同。
is_new
比如在各大接口文档自动生成工具中,都是通过注解的方式提供,如下所示
一般情况下,自动生成接口文档的平台会提供私有部署方案,接口文档就会被托管在私有的服务器上。这种方式看起来很方便,但是部署和维护的成本也都是不可忽视的。能不能不依赖托管服务器呢 ?
经过调研发现Gitlab Wiki功能可以符合预期要求
Gitlab Wiki
Mock直译为伪造,表示虚假的含义。在计算机技术中,数据Mock指通过伪造数据,实现预期目标。它最广泛的应用领域主要在接口Mock中。如下代码所示,通过返回接口虚假数据,让接口处于可用状态,方便前端调试调用
func GetUser() *model.RespBase { response := model.RespBase{ Code: 0, Data: model.QueryGetUserResp{ Uid: 88328876, Name: "Peter", Age: 45, Gender: 1, City: "New York", }, } return &response }
基于数据Mock,实现对接口的治理
在第二节《目标与调研》中我们分析了接口文档的组成,那么自动生成接口文档的第一步,就是需要先把接口文档抽象到程序中。
在如下程序中,定义的APIMock结构体即为一个接口的接口文档的抽象,也就是说整个接口文档会由[]APIMock组成,即多个APINock组成。
APIMock
[]APIMock
APINock
// RequestExplainItem 请求参数释义 type RequestExplainItem struct { Field string `json:"field"` Type string `json:"type"` IsMust bool `json:"is_must"` Mark string `json:"mark"` } // ResponseExplainItem 响应结构释义 type ResponseExplainItem struct { Field string `json:"field"` Type string `json:"type"` Mark string `json:"mark"` } // APIMock 接口文档抽象结构 type APIMock struct { Title string `json:"title"` Description string `json:"description"` Route string `json:"route"` Request interface{} `json:"request"` RequestExplain []RequestExplainItem `json:"request_explain"` Response interface{} `json:"response"` ResponseExplain []ResponseExplainItem `json:"response_explain"` }
举个例子,如果开发了一个/api/get_user接口,那么创建的APIMock对象如下所示
/api/get_user
apiMock := apimock.APIMock{ Title: "获取用户接口", Description: "获取用户接口", Route: "/api/get_user", Request: request, RequestExplain: []apimock.RequestExplainItem{ { Field: "uid", Type: "int", IsMust: true, Mark: "用户id", }, } Response: response, ResponseExplain: []apimock.ResponseExplainItem{ { Field: "data.uid", Type: "int", Mark: "用户ID", }, { Field: "data.name", Type: "string", Mark: "用户昵称", }, }, }
在APIMock的对象结构体中,有一个Request对象和一个Response对象。这两个对象是对接口请求参数和响应结构的抽象。 为什么需要定义这两个对象呢,下面会从两个方面来阐述原因
Request
Response
接口定义是指定义接口的路由,请求参数和响应结构。 在PHP等弱类型语言中,接口定义这个概念的应用不多,因为所有接口参数和响应结构用$array = array()这样的数组类型就能满足需要。但是对于Go等强类型语言来说,接口定义是一个接口的必要组成。
$array = array()
// QueryGetUserReq 接口请求参数结构 type QueryGetUserReq struct { Uid int `json:"uid"` } // QueryGetUserReq 接口请求响应结构 type QueryGetUserResp struct { Uid int `json:"uid"` Name string `json:"name"` }
很多情况下,接口的请求参数和响应结构都是比较复杂的嵌套结构(如下图所示)。在使用Wiki维护接口文档的时代,经常需要靠人力去编写和修改这些结构,这是一件非常不友好且极其低效率的事情。
为了解决这个问题,Request对象和Response对象的重要性在此就体现出来了,直接使用json.Marshal()编码就能获取到接口文档中的这些请求参数和响应结构。
json.Marshal()
如果当前接口请求地址中有mock_app和mock_token这两个参数且校验通过,则说明这个请求为Mock请求。
mock_app
mock_token
CURL -X GET 'https://test.api.com/api/get_user?mock_app=test&mock_token=avhsuekfiabs'
如果当前请求是Mock请求,那么调用Mock逻辑返回Mock数据。如下所示直接New一个自定义的MockResponse对象返回即可。
New
func returnMockGetUser() Response { response := model.RespBase{ Code: 0, Data: model.QueryGetUserResp{ Uid: 88328876, Name: "Peter", }, } }
如果当前请求不是Mock请求,那么执行正常业务逻辑,返回正常业务数据。
在获取到[]APIMock结构后,通过循环遍历拼接Markdown格式的方式,即可在代码仓库下生成自定义结构的接口文档。
Markdown
// 生成文档内容 for index, apiMock = range apiMockList { // 接口描述部分 contentList = append(contentList, "\n### (1) Description\n") contentList = append(contentList, apiMock.Description+"\n") // 接口地址部分 contentList = append(contentList, "\n### (2) URL\n") contentList = append(contentList, apiMock.Route+"\n") } // 写入apidoc.md文件 var content = strings.Join(contentList, "") os.WriteFile("apidoc.md", result, 0666)
在本地生成自定义结构的接口文档后,把它更新至Gitlab会包括两部分,第一是指随着代码的提交而更新Gitlab仓库中的apidoc.md文件,第二是指通过Rest API更新Gitlab Wiki。
apidoc.md
这样无论是仓库中的apidoc.md文件,还是Gitlab Wiki,都可以自由的选择使用。
如果项目支持CI,可以非常方便的把接口文档自动生成和更新至Gitlab的这两个逻辑都集成至CI中,可以参考CIManager项目的.gitlab/apidoc_gen.sh
简单总结下,这种设计方案主要包括4个关键点
APIMock/Request/Response
本设计方案的实现过程,请实现请参考项目apimock。假设现在你有一个项目,目录结构如下所示,或直接查看原项目代码
其中,所有接口的基础返回结构只有code/data/is_mock三个字段
code/data/is_mock
type RespBase struct { Code int `json:"code"` Data interface{} `json:"data"` IsMock bool `json:"is_mock"` }
现在需要新增一个/api/get_user接口,如何在你的项目中使用此方案呢 ?
首先找到Handler文件/server/handler.go,然后在接口对应的Handler中判断当前请求是否为Mock请求,如果是Mock请求,则返回Mock数据,如调用mock.GetUser()方法。否则就调用正常的业务逻辑,如调用service.GetUser(uidInt)方法。
/server/handler.go
mock.GetUser()
service.GetUser(uidInt)
func GetUserHandler(w http.ResponseWriter, r *http.Request) { var isMock = mock.IsMockRequest(r) uidString := r.URL.Query().Get("uid") uidInt, err := strconv.ParseInt(uidString, 10, 64) if err != nil { ResponseError(w, isMock, 100) return } if isMock { ResponseOk(w, isMock, mock.GetUser()) return } else { ResponseOk(w, isMock, service.GetUser(uidInt)) return } }
在定义mock.GetUser()函数前,需要先在/model/model.go文件中定义接口的请求结构体和响应结构体,如下所示。
/model/model.go
type QueryGetUserReq struct { Uid int64 `json:"uid"` } type QueryGetUserResp struct { Uid int64 `json:"uid"` Name string `json:"name"` Age uint8 `json:"age"` Gender uint8 `json:"gender"` City string `json:"city"` }
在定义完接口的请求和响应结构体后,先在根目录下创建/mock/mock.go文件,之后就可以开始按照如下步骤,在/mock/mock.go文件中定义mock.GetUser()函数了。
/mock/mock.go
func GetUser() *apimock.APIMock { // 1、创建请求对象 request := model.QueryGetUserReq{ Uid: 87654321, } // 2、创建响应对象 response := model.RespBase{ Code: 0, Data: model.QueryGetUserResp{ Uid: 88328876, Name: "Peter", Age: 45, Gender: 1, City: "New York", }, } // 3、创建apiMock对象 apiMock := apimock.APIMock{ // 接口名称 Title: "获取用户接口", // 接口描述 Description: "获取用户接口", // 接口路由 Route: "/api/get_user", // 请求对象和请求对象的字段释义 Request: request, RequestExplain: []apimock.RequestExplainItem{ { Field: "uid", // 字段名称 Type: "int", // 字段类型 IsMust: true, // 是否必填 Mark: "用户id", // 字段备注 }, }, // 响应对象和响应对象的字段释义 Response: response, ResponseExplain: []apimock.ResponseExplainItem{ { Field: "data.uid", Type: "int", Mark: "用户ID", }, { Field: "data.name", Type: "string", Mark: "用户昵称", }, { Field: "data.age", Type: "int", Mark: "用户年龄", }, { Field: "data.gender", Type: "int", Mark: "用户性别, 1女, 2男", }, { Field: "data.city", Type: "string", Mark: "用户所在城市", }, }, } return &apiMock }
在mock目录下创建mock.go文件对应的/mock/mock_test.go单元测试文件,然后定义TestAPIDocGen()函数
mock
mock.go
/mock/mock_test.go
TestAPIDocGen()
func TestAPIDocGen(t *testing.T) { var apiMockList []*apimock.APIMock // 生成所有的apiMock,并生成Markdown格式的字符串文档 apiMock := GetUser() apiMockList = append(apiMockList, apiMock) result, err := apimock.GenerateAPIDocument(apimock.APIDDocumentFormatMarkdown, apiMockList) if err != nil { t.Fail() return } // 文档写入至本地文件中 if err = os.WriteFile("../apidoc.md", result, 0666); err != nil { t.Fail() return } }
这样,使用go test时即可自动在本地生成apidoc.md接口文档了。
go test
恭喜,到这一步已经完成了所有需要的操作。你可以选择自己执行/mock/mock_test.go中的TestAPIDocGen()函数更新接口文档,也可以选择由CI自动完成Gitlab Wiki的更新
本文主要设计并实现了一种基于数据Mock的接口治理方案,不但解决了接口文档自动生成的问题,而且还可以满足前端人员的Mock接口需求。
更值得一提的是,这种设计更是一种通用的解决方案,它不局限于Go语言,在其他语言中也可以适用,非常简洁,通过轻量的方式即可实现。
The text was updated successfully, but these errors were encountered:
简单总结下,就是可以实现如下功能
Sorry, something went wrong.
No branches or pull requests
前言
本文原创,著作权归WGrape所有,未经授权,严禁转载
一、背景
在《关于接口文档高效治理方案的研究和思考》一文中已经详细介绍了高效管理接口文档的重要性,更多内容可以阅读原文。这里就不再解释为什么需要,而是重点关注怎么做。
在本文接下来的内容中,会为大家分享一种轻量级的创新型方案,它不但可以简单高效实现接口文档的自动生成,而且可以满足前端人员Mock接口的需要。这就是基于数据Mock的接口治理方案,请慢慢往下看,大约会花费8分钟的时间。
二、目标与调研
1、自动生成接口文档
接口治理的第一个目标就是实现自动生成接口文档。后端人员只需在开发接口的同时按照规范编写相应逻辑,即可在CI阶段自动生成接口文档。
2、满足Mock接口的需要
接口治理的第二个目标就是满足前端人员Mock接口的需要。无论是开发、联调、测试、上线后的哪一个阶段,前端都可以根据自己的需要,随时Mock后端的接口。
3、设计调研
(1) 接口文档的组成
我们知道,无论是什么类型的接口文档,其必须具备以下组成元素
不过有些信息比如参数
is_new
表示是否为新用户,这些信息都是属于程序无法自动获取的自定义内容,它们还是需要人力提供的,只是提供的方式可能有所不同。比如在各大接口文档自动生成工具中,都是通过注解的方式提供,如下所示
(2) 接口文档的托管
一般情况下,自动生成接口文档的平台会提供私有部署方案,接口文档就会被托管在私有的服务器上。这种方式看起来很方便,但是部署和维护的成本也都是不可忽视的。能不能不依赖托管服务器呢 ?
经过调研发现
Gitlab Wiki
功能可以符合预期要求三、什么是数据Mock
Mock直译为伪造,表示虚假的含义。在计算机技术中,数据Mock指通过伪造数据,实现预期目标。它最广泛的应用领域主要在接口Mock中。如下代码所示,通过返回接口虚假数据,让接口处于可用状态,方便前端调试调用
四、方案设计
1、设计思想
基于数据Mock,实现对接口的治理
2、接口文档抽象
在第二节《目标与调研》中我们分析了接口文档的组成,那么自动生成接口文档的第一步,就是需要先把接口文档抽象到程序中。
在如下程序中,定义的
APIMock
结构体即为一个接口的接口文档的抽象,也就是说整个接口文档会由[]APIMock
组成,即多个APINock
组成。举个例子,如果开发了一个
/api/get_user
接口,那么创建的APIMock
对象如下所示3、Request对象和Response对象
在
APIMock
的对象结构体中,有一个Request
对象和一个Response
对象。这两个对象是对接口请求参数和响应结构的抽象。 为什么需要定义这两个对象呢,下面会从两个方面来阐述原因(1) 作为接口定义的一部分
接口定义是指定义接口的路由,请求参数和响应结构。 在PHP等弱类型语言中,接口定义这个概念的应用不多,因为所有接口参数和响应结构用
$array = array()
这样的数组类型就能满足需要。但是对于Go等强类型语言来说,接口定义是一个接口的必要组成。(2) 作为接口文档的一部分
很多情况下,接口的请求参数和响应结构都是比较复杂的嵌套结构(如下图所示)。在使用Wiki维护接口文档的时代,经常需要靠人力去编写和修改这些结构,这是一件非常不友好且极其低效率的事情。
为了解决这个问题,
Request
对象和Response
对象的重要性在此就体现出来了,直接使用json.Marshal()
编码就能获取到接口文档中的这些请求参数和响应结构。4、Mock请求与非Mock请求
如果当前接口请求地址中有
mock_app
和mock_token
这两个参数且校验通过,则说明这个请求为Mock请求。CURL -X GET 'https://test.api.com/api/get_user?mock_app=test&mock_token=avhsuekfiabs'
(1) 是Mock请求
如果当前请求是Mock请求,那么调用Mock逻辑返回Mock数据。如下所示直接
New
一个自定义的MockResponse
对象返回即可。(2) 不是Mock请求
如果当前请求不是Mock请求,那么执行正常业务逻辑,返回正常业务数据。
5、生成Markdown格式文档
在获取到
[]APIMock
结构后,通过循环遍历拼接Markdown
格式的方式,即可在代码仓库下生成自定义结构的接口文档。6、自动更新至Gitlab
在本地生成自定义结构的接口文档后,把它更新至Gitlab会包括两部分,第一是指随着代码的提交而更新Gitlab仓库中的
apidoc.md
文件,第二是指通过Rest API更新Gitlab Wiki
。这样无论是仓库中的
apidoc.md
文件,还是Gitlab Wiki
,都可以自由的选择使用。7、集成至CI
如果项目支持CI,可以非常方便的把接口文档自动生成和更新至Gitlab的这两个逻辑都集成至CI中,可以参考CIManager项目的.gitlab/apidoc_gen.sh
8、总结
简单总结下,这种设计方案主要包括4个关键点
APIMock/Request/Response
等对象,巧妙利用Json解析等技术自动生成文接口档Response
还可以用来作为Mock接口的返回五、实现过程
本设计方案的实现过程,请实现请参考项目apimock。假设现在你有一个项目,目录结构如下所示,或直接查看原项目代码
其中,所有接口的基础返回结构只有
code/data/is_mock
三个字段现在需要新增一个
/api/get_user
接口,如何在你的项目中使用此方案呢 ?六、如何使用
1、填充Handler
首先找到Handler文件
/server/handler.go
,然后在接口对应的Handler中判断当前请求是否为Mock请求,如果是Mock请求,则返回Mock数据,如调用mock.GetUser()
方法。否则就调用正常的业务逻辑,如调用service.GetUser(uidInt)
方法。2、定义接口的请求和响应结构体
在定义
mock.GetUser()
函数前,需要先在/model/model.go
文件中定义接口的请求结构体和响应结构体,如下所示。3、定义mock.GetUser()函数
在定义完接口的请求和响应结构体后,先在根目录下创建
/mock/mock.go
文件,之后就可以开始按照如下步骤,在/mock/mock.go
文件中定义mock.GetUser()
函数了。4、定义TestAPIDocGen函数
在
mock
目录下创建mock.go
文件对应的/mock/mock_test.go
单元测试文件,然后定义TestAPIDocGen()
函数这样,使用
go test
时即可自动在本地生成apidoc.md
接口文档了。5、完成
恭喜,到这一步已经完成了所有需要的操作。你可以选择自己执行
/mock/mock_test.go
中的TestAPIDocGen()
函数更新接口文档,也可以选择由CI自动完成Gitlab Wiki
的更新七、总结
本文主要设计并实现了一种基于数据Mock的接口治理方案,不但解决了接口文档自动生成的问题,而且还可以满足前端人员的Mock接口需求。
APIMock/Request/Response
等对象,巧妙利用Json解析等技术自动生成文接口档Response
还可以用来作为Mock接口的返回更值得一提的是,这种设计更是一种通用的解决方案,它不局限于Go语言,在其他语言中也可以适用,非常简洁,通过轻量的方式即可实现。
The text was updated successfully, but these errors were encountered: