写在最前面:尽管beego有人不喜欢,也存在一些被人诟病的问题(少部分情况下),但是并不妨碍我们的学习使用。如果你的项目比较复杂且对性能不精益求精,可以考虑使用beego。
初步使用beego框架,参照了官网文档教程,将结合自己的实际使用及个人理解,对beego api自动化文档做个笔记。
官方链接 : https://beego.me/docs/intro/
一、安装beego:命令行输入,网络强悍应该可以get下来,我是在$GOPATH/src下操作的。
go get github.com/astaxie/beego
二、牛皮的bee工具的安装:
go get github.com/beego/bee
三、新建一个bee API项目:
bee api beeapi
不出意外,命令行一顿操作之后,应该在$GOPATH/src下面操作出了一个beeapi文件夹。用IDE打开这个project,可以看到如下结构:
beego app是标准的MVC三层结构,bee api只是把View去掉了。
四、稍微研究了下自动生成的Controller和对应的Models后,又经过老师的解释,我认为,Controller负责接受客户端的http请求,然后models里面的东西就负责具体的操作。Models操作完,把结果给Controller,Controller再把结果返回给客户端。因而Controller就相当于中继了。
接下来进行照葫芦画瓢,在controller和models下同时新建一个叫作weapon的go文件。
先来操作一下controller:
在“葫芦”/controller/user.go中,看到如下代码:
// Operations about Users
type UserController struct {
beego.Controller
}
// @Title CreateUser
// @Description create users
// @Param body body models.User true "body for user content"
// @Success 200 {int} models.User.Id
// @Failure 403 body is empty
// @router / [post]
func (u *UserController) Post() {
var user models.User
json.Unmarshal(u.Ctx.Input.RequestBody, &user)
uid := models.AddUser(user)
u.Data["json"] = map[string]string{"uid": uid}
u.ServeJSON()
}
// @Title GetAll
// @Description get all Users
// @Success 200 {object} models.User
// @router / [get]
func (u *UserController) GetAll() {
users := models.GetAllUsers()
u.Data["json"] = users
u.ServeJSON()
}
不全部粘贴,相互类似。
那么我们在自己的weapon中:
先操作出一个controller叫做WeaponController:
//Operation About Weapon,When WeaponController Contains beego.Controller,
//It Will Automaticly Poccess All beego.Controller's Methods.Sounds Like Inherit.
type WeaponController struct {
beego.Controller
}
再操作一下它的一些方法:
//***These comments are so bad ass, explain later***
// @Title GetAll (--This comment is title--)
// @Description get all Weapons (--This is description about this method--)
// @Success 200 {object} models.Weapon (--Success status--)
// @router / [get] (--IMPORTANT:this shows its sub-route in /v1/weapon/,(Explain //later),and its http operation is GET--)
func (w *WeaponController) GetAll() {
//Call Model's function and get some returned value
weapons := models.GetAllWeapons()
//Set w.Data to returned value
w.Data["json"] = weapons
//Response
w.ServeJSON()
}
// @Title GetWeapon
// @Description get weapon by id
// @Param wid path string true "The key for staticblock"
//About Param: variable's name, inputType(in http), dataType, is it neccessary, //description
// @Success 200 {object} models.Weapon
// @Failure 403 :wid is empty
// @router /:wid [get] (This shows its route is :/v1/weapon/:wid, and ":wid" should //be replaced with a weaponID)
func (w *WeaponController) GetWeapon(){
wid ,err:= w.GetInt(":wid")
if err == nil {
weapon, err := models.GetWeapon(wid)
if err != nil {
w.Data["json"] = err.Error()
} else {
w.Data["json"] = weapon
}
}
w.ServeJSON()
}
// @Title AddWeapon
// @Description create a new weapon
// @Param body body models.Weapon true "body for weapon content"
// @Success 200 {int} models.Weapon.Id
// @Failure 403 body is empty
// @router / [post]
func (w *WeaponController) AddWeapon(){
var weapon models.Weapon
json.Unmarshal(w.Ctx.Input.RequestBody, &weapon)
wid := models.AddWeapon(&weapon)
w.Data["json"] = map[int]int{wid: wid}
w.ServeJSON()
}
然后是,models:
//This part is meant to process logic tasks or other things, MVC-M.Convenient to //manage project
//This part usually define a type of some resource,such as "Weapon",then do all the //detailed work
//controller tells to do, and give back a specific result to its controller's method
//Define a WeaponList with key=weapons'id to store all the weapons
var WeaponList map[int] *Weapon
//Define what does a weapon have
type Weapon struct {
Id int
Name string
Range int
Type string
Mags int
Weight int
}
//initialization: Add a sample of weapon
func init(){
WeaponList = make(map[int] *Weapon)
w := Weapon{
201801,
"Desert.Eagle",
50,
"pistol",
7,
600,
}
WeaponList[w.Id] = &w
}
//Return all weapons in WeaponList
func GetAllWeapons() map[int] *Weapon{
return WeaponList
}
//Select a specific weapon from WeaponList by its Id
func GetWeapon(wth int) (*Weapon,error){
if w, ok :=WeaponList[wth];ok {
return w, nil
}
return nil,errors.New("No such Weapon!")
}
//Add a brand new kind of weapon to WeaponList
func AddWeapon(w *Weapon) int{
WeaponList[w.Id]=w
return w.Id
}
好了,现在开始把所有的东西串起来了:
点开terminal,在当前项目目录下面:
操作这个
bee run -gendoc=true -downdoc=true
然后等他操作。。。
操作完成后,我们注意到,工程目录下多了一个swagger文件夹。还有routers目录下面多了一个comment。。。go文件,打开这个comment...go:里面有很多长得很像的东西:
beego.GlobalControllerRouter["beeapi2/controllers:UserController"] = append(beego.GlobalControllerRouter["beeapi2/controllers:UserController"],
beego.ControllerComments{
Method: "Logout",
Router: `/logout`,
AllowHTTPMethods: []string{"get"},
Params: nil})
仔细观察,厉害了,他好像是根据我们之前的bad ass注释生成的,router.go里面解释了为什么之前的注释里面写的路径在“/v1/weapon/”下面,这是遵循了RESTful风格定的名字和路径。
五、最后,在浏览器:localhost:8080/swagger,摸索一下你就会发现,这里就是测试你写的beego api的地方。
-------------------------------------------------
我遇到过的问题:
1、beego如果发现修改了API,重新bee run -gendoc=true之后而swagger并没有更新改变,请清除浏览器的缓存(或chrome的Ctrl+F5)再试。WC这个坑了我好久好多次。。。
2、ONE: beego用bee run -gendoc=true 没有生成路由commentsRouter.go文件或更新生成的swagger, 或者在访问API的时候报404 not found:在conf/app.conf配置文件中runmode改成dev模式再执行命令; 还有EnableDocs = true。
TWO: 检查-gendoc=true拼写是否正确。
THREE:如果你使用了gomod(现在是2020年8月20日,beego最新版本v1.12.2,尚未正式解决gomod相关问题),且项目没在gopath/src里面,先尝试将beego版本在go.mod中手动指定为v1.12.1或者更低版本。如果不行,可以尝试按照搜索引擎搜出来的方法试试(全TM互相复制粘贴的,我试过了,可能我这边情况有点不一样,所以,没用)。如果最后还没解决,请检查在bee run -gendoc=true 执行过程中是否有panic,如果有panic,则bee可能无法执行到生成路由文档那一步
3、又发现一个坑:我修改了controller的一个小逻辑,又打了个log,但无论重新go build 还是 bee run -gendoc=true ,最后在调这个controller的这个 API 时,改过的逻辑并没有改变,而且log也没有打出来,删除可执行文件再试还是没改。。。灵异事件?最后我把router_comment删了,重新bee run -gendoc=true就好了。
4、又更新一个稍不注意就会遇到并觉得十分诡异的坑:swagger上面的api点击之后调用的却是另一个api, 呈现调用错位的错觉,且没有什么规律。原因:controller里面的路由注释中的@Title部分出现了重复。beego swagger使用Title来指定唯一映射。解决方法:将所有的Title重写成唯一的,或者干脆将所有的Title给删除了,对没错,可以不用写Title。