beego API开发以及自动化文档
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
beego API開發(fā)以及自動化文檔
beego1.3版本已經(jīng)在上個星期發(fā)布了,但是還是有很多人不了解如何來進(jìn)行開發(fā),也是在一步一步的測試中開發(fā),期間QQ群里面很多人都問我如何開發(fā),我的業(yè)余時間實(shí)在是排的太滿了,實(shí)在是沒辦法一一回復(fù)大家,在這里和大家說聲對不起,這兩天我又不斷的改進(jìn),寫了一個應(yīng)用示例展示如何使用beego開發(fā)API已經(jīng)自動化文檔和測試,這里就和大家詳細(xì)的解說一下。
自動化文檔開發(fā)的初衷
我們需要開發(fā)一個API應(yīng)用,然后需要和手機(jī)組的開發(fā)人員一起合作,當(dāng)然我們首先想到的是文檔先行,我們也根據(jù)之前的經(jīng)驗(yàn)寫了我們需要的API原型文檔,我們還是根據(jù)github的文檔格式寫了一些漂亮的文檔,但是我們開始擔(dān)心這個文檔如果兩邊不同步怎么辦?因?yàn)楫吘故窃臀臋n,變動是必不可少的。手機(jī)組有一個同事之前在雅虎工作過,他推薦我看一個swagger的應(yīng)用,看了swagger的標(biāo)準(zhǔn)和文檔化的要求,感覺太棒了,這個簡直就是神器啊,通過swagger可以方便的查看API的文檔,同時使用API的用戶可以直接通過swagger進(jìn)行請求和獲取結(jié)果。所以我就開始學(xué)習(xí)swagger的標(biāo)準(zhǔn),同時開始進(jìn)行Go源碼的研究,通過Go里面的AST進(jìn)行源碼分析,針對comments解析,然后生成swagger標(biāo)準(zhǔn)的json格式,這樣最后就可以和swagger完美結(jié)合了。
這樣做的好處有三個:
注釋標(biāo)準(zhǔn)化
有了注釋之后,以后API代碼維護(hù)相當(dāng)方便
根據(jù)注釋自動化生成文檔,方便調(diào)用的用戶查看和測試
beego API應(yīng)用入門
請大家更新到最新的bee和beego
go?get?-u?github.com/beego/bee go?get?-u?github.com/astaxie/beego然后進(jìn)入到你的GOPATH/src目錄,執(zhí)行命令bee api bapi,進(jìn)入目錄cd bapi,執(zhí)行命令bee run -downdoc=true -docgen=true.請看下面我執(zhí)行的效果圖:
執(zhí)行完成之后就打開瀏覽器,輸入URL:http://127.0.0.1:8080/swagger/swagger-1/
記住這里必須要用127.0.0.1,不能使用localhost,存在CORS問題,Ajax跨域
我們的效果和應(yīng)用都出來了,很酷很炫吧,那這后面到底采用了怎么樣的一些技術(shù)呢?讓我們一步一步來講解這些細(xì)節(jié):
項(xiàng)目目錄
我們首先來了解一下bee api創(chuàng)建的應(yīng)用的目錄結(jié)構(gòu):
|--?bapi |--?conf |???`--?app.conf |--?controllers |???|--?object.go |???`--?user.go |--?docs |???|--?doc.go |???`--?docs.go |--?lastupdate.tmp |--?main.go |--?models |???|--?object.go |???`--?user.go |--?routers |???|--?commentsRouter.go |???`--?router.go |--?swagger `--?tests`--?default_test.gomain.go 是程序的統(tǒng)一入口文件
bapi 是生成的二進(jìn)制文件
conf 配置文件目錄,app.conf
controllers 控制器目錄,主要是邏輯的處理
models 是數(shù)據(jù)處理層的目錄
docs 是自動化生成文檔的目錄
lastupdate.tmp 是一個注解路由的緩存文件
routers是路由目錄,主要涉及一些路由規(guī)則
swagger 是一個html靜態(tài)資源目錄,是通過bee自動下載的,主要就是展示我們看到的界面及測試
test 目錄是針對應(yīng)用的測試用例,beego相比其他revel框架的好處之一就是無需啟動應(yīng)用就可以執(zhí)行test case。
入口文件main
我們第一步先來看一下入口是怎么寫的?
package?mainimport?(_?"bapi/docs"_?"bapi/routers""github.com/astaxie/beego" )func?main()?{if?beego.RunMode?==?"dev"?{beego.DirectoryIndex?=?truebeego.StaticDir["/swagger"]?=?"swagger"}beego.Run() }入口文件就是一個普通的beego應(yīng)用的標(biāo)準(zhǔn)代碼,只是這里多了幾行代碼,把swagger加入了static,因?yàn)槲覀冃枰盐臋n服務(wù)器集成到beego的API應(yīng)用中來。然后增加了docs的初始化引入,和router的效果一樣。接下里我們先來看看自動化API的路由是怎么設(shè)計(jì)的
namespace路由
自動化路由才有了namespace來進(jìn)行設(shè)計(jì),而且注意兩點(diǎn),第一目前只支持namespace的路由支持自動化文檔,第二只支持NSNamespace和NSInclude解析,而且是只能兩個層級,先看我們的路由設(shè)置:
func?init()?{ns?:=?beego.NewNamespace("/v1",beego.NSNamespace("/object",beego.NSInclude(&controllers.ObjectController{},),),beego.NSNamespace("/user",beego.NSInclude(&controllers.UserController{},),),)beego.AddNamespace(ns) }我們先來看一下這個代碼,首先是使用beego.NewNamespace創(chuàng)建一個ns的變量,這個變量里面其實(shí)就是存儲了一棵路由樹,我們可以把這棵樹加到其他任意已經(jīng)存在的樹中去,這也就是namespace的好處,可以在任意的模塊中設(shè)計(jì)自己的namespace,然后把這個namespace加到其他的應(yīng)用中去,可以增加任意的前綴等。
這里我們分析一下NewNamespace這個函數(shù),這個函數(shù)的定義是這樣的NewNamespace(prefix string, params ...innnerNamespace) *Namespace,他的第一個參數(shù)就是前綴,第二個參數(shù)是innnerNamespace多參數(shù),那么我們來看看innnerNamespace的定義是什么:
type?innnerNamespace?func(*Namespace)它是一個函數(shù),也就是只要是符合參數(shù)是*Namespace的函數(shù)都可以。那么在beego里面定義了如下的方法支持返回這個函數(shù)類型:
NSCond(cond namespaceCond) innnerNamespace
NSBefore(filiterList ...FilterFunc) innnerNamespace
NSAfter(filiterList ...FilterFunc) innnerNamespace
NSInclude(cList …ControllerInterface) innnerNamespace
NSRouter(rootpath string, c ControllerInterface, mappingMethods …string) innnerNamespace
NSGet(rootpath string, f FilterFunc) innnerNamespace
NSPost(rootpath string, f FilterFunc) innnerNamespace
NSDelete(rootpath string, f FilterFunc) innnerNamespace
NSPut(rootpath string, f FilterFunc) innnerNamespace
NSHead(rootpath string, f FilterFunc) innnerNamespace
NSOptions(rootpath string, f FilterFunc) innnerNamespace
NSPatch(rootpath string, f FilterFunc) innnerNamespace
NSAny(rootpath string, f FilterFunc) innnerNamespace
NSHandler(rootpath string, h http.Handler) innnerNamespace
NSAutoRouter(c ControllerInterface) innnerNamespace
NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace
NSNamespace(prefix string, params …innnerNamespace) innnerNamespace
因此我們可以在NewNamespace這個函數(shù)的第二個參數(shù)列表中使用上面的任意函數(shù)作為參數(shù)調(diào)用。
我們看一下路由代碼,這是一個層級嵌套的函數(shù),第一個參數(shù)是/v1,即為/v1開頭的路由樹,第二個參數(shù)是beego.NSNamespace,第三個參數(shù)也是beego.NSNamespace,也就是路由樹嵌套了路由樹,而我們的beego.NSNamespace里面也是和NewNamespace一樣的參數(shù),第一個參數(shù)是路由前綴,第二個參數(shù)是slice參數(shù)。這里我們調(diào)用了beego.NSInclude來進(jìn)行注解路由的引入,這個函數(shù)是專門為注解路由設(shè)計(jì)的,我們可以看到這個設(shè)計(jì)里面我們沒有任何的路由信息,只是設(shè)置了前綴,那么這個的路由是在哪里設(shè)置的呢?我們接下來分析什么是注解路由。
注解路由
可能有些同學(xué)不了解什么是注解路由,也就是在Controller類上添加一個注釋讓框架給自動添加Route,那么我們來看一下ObjectController和UserController中怎么寫路由注解的:
//?Operations?about?object type?ObjectController?struct?{beego.Controller }//?@Title?create //?@Description?create?object //?@Param???body????????body????models.Object???true????????"The?object?content" //?@Success?200?{string}?models.Object.Id //?@Failure?403?body?is?empty //?@router?/?[post] func?(this?*ObjectController)?Post()?{var?ob?models.Objectjson.Unmarshal(this.Ctx.Input.RequestBody,?&ob)objectid?:=?models.AddOne(ob)this.Data["json"]?=?map[string]string{"ObjectId":?objectid}this.ServeJson() }我們看到我們的每一個函數(shù)上面有大段的注釋,注解路由其實(shí)主要關(guān)注最后一行// @router / [post],這一行的注釋就是表示這個函數(shù)是注冊到路由/,支持方法是post。
和我們平常的時候使用beego.Router("/", &ObjectController{},"post:Post")的效果是一模一樣的,只是這一次框架幫你自動注冊了這樣的路由,框架是如何來自動注冊的呢?在應(yīng)用啟動的時候,會判斷是否有調(diào)用NSInclude,在調(diào)用的時候,判斷RunMode是否是dev模式,是的話就會判斷是否之前有分析過,并且分析對象目錄有更新,就使用Go的AST進(jìn)行源碼分析(當(dāng)然只分析NSInclude調(diào)用的controller),然后生成文件routers/commentsRouter.go,在該文件中會自動注冊我們需要的路由信息。這樣就完成了整個的注解路由注冊。
注解路由是使用// @router 開頭來申明的,而且必須放在你要注冊的函數(shù)的上方,和其他注釋@Title @Description的順序無關(guān),你可以放在第一行,也可以最后一行。有兩個參數(shù),第一個是需要注冊的路由,第二個是支持的方法。
路由可以支持beego支持的任意規(guī)則,例如/object/:key這樣的參數(shù)路由,也可以固定路由/object,也可以是正則路由/cms_:id([0-9]+).html
支持的HTTP方法必須使用[]中間是支持的方法列表,多個使用,分割,例如[post,get]。但是目前自動化文檔只支持一個方法,也就是你多個的方法的時候無法做到RESTFul到同一個函數(shù),也不鼓勵你這樣設(shè)計(jì)的API。如果你API設(shè)計(jì)的時候支持了多個方法,那么文檔生成的時候默認(rèn)是取第一個作為支持的方法。
上面我們看到我們的方法上面有很多注釋,那么接下來就進(jìn)入我們今天的重點(diǎn):自動化文檔
自動化文檔
所謂的自動化文檔,說白了就是根據(jù)我們的注釋自動的生成我們可以看得懂的漂亮文檔。我們上面也說了寫注釋不僅僅是方便我們的代碼維護(hù),邏輯闡述,同時如果能夠自動生成文檔,那么對于使用API的用戶來說也是很大的幫助。那么如何進(jìn)行自動化文檔生成呢?
我當(dāng)初看了swagger的展示效果之后,首先研究了他的spec,發(fā)現(xiàn)是一些json數(shù)據(jù),只要我們的API能夠生成swagger認(rèn)識的json就可以了,因此我的思路就來了,根據(jù)注釋生成swagger的JSON標(biāo)準(zhǔn)數(shù)據(jù)輸出。swagger提供了一個例子代碼:petstore?我就是根據(jù)這個例子的格式一步一步實(shí)現(xiàn)了現(xiàn)在的自動化文檔。
首先第一步就是API的描述:
API文檔
我們看到在router.go里面頭部有一大段的注釋,這些注釋就是描述整個項(xiàng)目的一些信息:
//?@APIVersion?1.0.0 //?@Title?beego?Test?API //?@Description?beego?has?a?very?cool?tools?to?autogenerate?documents?for?your?API //?@Contact?astaxie@gmail.com //?@TermsOfServiceUrl?http://beego.me/ //?@License?Apache?2.0 //?@LicenseUrl?http://www.apache.org/licenses/LICENSE-2.0.html這里面主要是幾個標(biāo)志:
@APIVersion
@Title
@Description
@Contact
@TermsOfServiceUrl
@License
@LicenseUrl
這里每一個都不是必須的,你可以寫也可以不寫,后面就是一個字符串,你可以使用任意喜歡的字符進(jìn)行描述。我們來看一下生成的:http://127.0.0.1:8080/docs
{"apiVersion":?"1.0.0","swaggerVersion":?"1.2","apis":?[{"path":?"/object","description":?"Operations?about?object\n"},{"path":?"/user","description":?"Operations?about?Users\n"}],"info":?{"title":?"beego?Test?API","description":?"beego?has?a?very?cool?tools?to?autogenerate?documents?for?your?API","contact":?"astaxie@gmail.com","termsOfServiceUrl":?"http://beego.me/","license":?"Url?http://www.apache.org/licenses/LICENSE-2.0.html"} }這是首次請求的一些信息,那么apis是怎么來的呢?這個就是根據(jù)你的namespace進(jìn)行源碼AST分析獲取的,所以目前只支持兩層的namespace嵌套,而且必須是兩層,第一層是baseurl,第二層就是嵌套的namespace的prefix。也就是上面的path信息,那么里面的description那里獲取的呢?請看控制器的注釋,
控制器注釋文檔
針對每一個控制我們可以增加注釋,用來描述該控制器的作用:
//?Operations?about?object type?ObjectController?struct?{beego.Controller }這個注釋就是用來表示我們的每一個控制器API的作用,而控制器的函數(shù)里面的注釋就是用來表示調(diào)用的路由、參數(shù)、作用以及返回的信息。
//?@Title?Get //?@Description?find?object?by?objectid //?@Param???objectId????????path????string??true????????"the?objectid?you?want?to?get" //?@Success?200?{object}?models.Object //?@Failure?403?:objectId?is?empty //?@router?/:objectId?[get] func?(this?*ObjectController)?Get()?{ }從上面的注釋我們可以把我們的注釋分為以下類別:
@Title
接口的標(biāo)題,用來標(biāo)示唯一性,唯一,可選
格式:之后跟一個描述字符串
@Description
接口的作用,用來描述接口的用途,唯一,可選
格式:之后跟一個描述字符串
@Param
請求的參數(shù),用來描述接受的參數(shù),多個,可選
格式:變量名 傳輸類型 類型 是否必須 描述
傳輸類型:
類型:
變量名和描述是一個字符串
是否必須:true 或者false
string
int
int64
對象,這個地方大家寫的時候需要注意,需要是相對于當(dāng)前項(xiàng)目的路徑.對象,例如models.Object表示models目錄下的Object對象,這樣bee在生成文檔的時候會去掃描改對象并顯示給用戶改對象。
query 表示帶在url串里面?aa=bb&cc=dd
form 表示使用表單遞交數(shù)據(jù)
path 表示URL串中得字符,例如/user/{uid} 那么uid就是一個path類型的參數(shù)
body 表示使用raw body進(jìn)行數(shù)據(jù)的傳輸
header 表示通過header進(jìn)行數(shù)據(jù)的傳輸
@Success
成功返回的code和對象或者信息
格式:code 對象類型 信息或者對象路徑
code:表示HTTP的標(biāo)準(zhǔn)status code,200 201等
對象類型:{object}表示對象,其他默認(rèn)都認(rèn)為是字符類型,會顯示第三個參數(shù)給用戶,如果是{object}類型,那么就會去掃描改對象,并顯示給用戶
對象路徑和上面Param中得對象類型一樣,使用路徑.對象的方式來描述
@Failure
錯誤返回的信息,
格式: code 信息
code:同上Success
錯誤信息:字符串描述信息
@router
上面已經(jīng)描述過支持兩個參數(shù),第一個是路由,第二個表示支持的HTTP方法
那么我們通過上面的注釋會生成怎么樣的JSON信息呢?
{"path":?"/object/{objectId}","description":?"","operations":?[{"httpMethod":?"GET","nickname":?"Get","type":?"","summary":?"find?object?by?objectid","parameters":?[{"paramType":?"path","name":?"objectId","description":?"\"the?objectid?you?want?to?get\"","dataType":?"string","type":?"","format":?"","allowMultiple":?false,"required":?true,"minimum":?0,"maximum":?0}],"responseMessages":?[{"code":?200,"message":?"models.Object","responseModel":?"Object"},{"code":?403,"message":?":objectId?is?empty","responseModel":?""}]}] }上面闡述的這些描述都是可以使用一個或者多個?'\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP)進(jìn)行分割
對象自定義注釋
我們的對象定義如下:
type?Object?struct?{ObjectId???stringScore??????int64PlayerName?string }通過掃描生成的代碼如下:
Object?{ ObjectId?(string,?optional):?, PlayerName?(string,?optional):?, Score?(int64,?optional): }我們發(fā)現(xiàn)字段都是optional的,而且沒有任何針對字段的描述,其實(shí)我們可以在對象定義里面增加如下的tag:
type?Object?struct?{ObjectId???string???`required:"true"?description:"object?id"`Score??????int64????????`required:"true"?description:"players's?scores"`PlayerName?string???`required:"true"?description:"plaers?name,?used?in?system"` }而且如果你的對象tag里面如果存在json或者thrift描述,那么就會使用改描述作為字段名,即如下的代碼:
type?Object?struct?{ObjectId???string???`json:"object_id"`Score??????int64????????`json:"player_score"`PlayerName?string???`json:"player_name"` }就會輸出如下的文檔信息:
Object?{ object_id?(string,?optional):?, player_score?(string,?optional):?, player_name?(int64,?optional): }常見錯誤及問題
Q:bee沒有上面執(zhí)行的命令?
A:請更新bee到最新版本,目前bee的版本是1.1.2,beego的版本是1.3.1
Q:bee更新的時候出錯了?
A:第一可能是GFW的問題,第二可能是你修改過了源碼,刪除重新下載,第三可能你升級了Go版本,你需要刪除GOPATH/pkg下的所有文件
Q:下載swagger很慢?
A:想辦法讓他變快,因?yàn)槲椰F(xiàn)在放在了github上面
Q:文檔生成了,但是我沒辦法測試請求?
A:你看看你訪問的地址是不是和請求的URL是同一個地址,因?yàn)閟wagger是使用Ajax請求數(shù)據(jù)的,所以跨域的問題,解決CORS的辦法就是保持域一致,包括URL和端口。
Q:運(yùn)行的時候發(fā)生了未知的錯誤?
A:那就來提issue或者給我留言吧,我會盡力幫助你解決你遇到的問題
轉(zhuǎn)載于:https://my.oschina.net/astaxie/blog/284072
總結(jié)
以上是生活随笔為你收集整理的beego API开发以及自动化文档的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 巧用svn create patch(打
- 下一篇: ORACLE数据库管理工具EM