技术周刊之基于beego web框架的RESTful API的构建之旅

前言

​ beego是一个快速开发GO应用的http框架,作者是go语言方向的大牛,astaxie。beego可以用来快速开发API、web、后端服务等应用,是一个RESTFul风格的框架,主要的设计灵感来自于Python web开发框架tornado、flask、sinstra,很好的结合了Go语言本身的一些特性(interface,struct继承等)。

​ beego是基于八大独立模块来实现的,很好的实现了模块间的解耦,即使用户不使用http的逻辑,也可以很好的使用其中的各个模块。作者自己说,他的这种思想来自于乐高积木,设计beego的时候,这些模块就是积木,而最终搭建好的机器人就是beego。

​ 这篇博文通过使用beego来构建API,讲解实现过程中的细节以及遇到的一些坑,让我们马上开始beego的API构建之旅吧!

项目创建

  • 进入到你的$GOPATH/src
  • 安装beego开发包自己快速开发工具bee
1
2
3
go get github.com/astaxie/beego
go get github.com/astaxie/beego/orm
go get github.com/beego/bee
  • 使用快速开发工具bee,创建我们的API项目
1
bee new firstAPI

我们得到的项目结构如下图所示:

可以看出这是一个典型的MVC架构的应用,beego把我们项目所需要的一些都准备好了,例如配置文件conf,测试文件tests等,我们只需要专注于API代码的编写即可。

运行项目并获得API自动化文档

1
bee run -gendoc=true -downdoc=true

运行上述代码输出如下图所示:

我们在浏览器中访问:本机IP:8080/swagger,就会看到swagger的API文档,我们代码更新后,该文档就会自动更新,非常方便。

models设计

  • 对 数据库object 操作有四个方法 Read / Insert / Update / Delete
1
2
3
4
5
6
7
8
9
10
11
示例代码:
o := orm.NewOrm()
user := new(User)
user.Name = "slene"

fmt.Println(o.Insert(user))

user.Name = "Your"
fmt.Println(o.Update(user))
fmt.Println(o.Read(user))
fmt.Println(o.Delete(user))

还有其他的方法可以参阅beego官方文档,里面对orm操作有着详细的介绍。

  • 创建一个数据库并设计一张数据库表
1
2
3
4
5
6
7
CREATE TABLE IF NOT EXISTS `student` (
`Id` int(11),
`Name` varchar(255),
`Birthdate` varchar(255),
`Gender` bool,
`Score` int(11)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 在models文件夹下新建一个文件Student.go,并实现以下代码,代码中关键点都有注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package models

import (
"fmt"
"github.com/astaxie/beego/orm"
)


//在models模块中创建一个struct,目的是使用beego的orm框架,使struct与数据库中的字段产生对应关系
type Student struct {
Id int`orm:"column(Id)"` //column()括号中的字段就是在定义数据库时的相应字段,这一段必须严格填写,不然在API读写数据时就会出现读不到或者写不进去的问题
Name string `orm:"column(Name)"`
BirthDate string `orm:"column(Birthdate)"`
Gender bool `orm:"column(Gender)"`
Score int `orm:"column(Score)"`
}


//该函数获得数据库中所有student的信息,返回值是一个结构体数组指针
func GetAllStudents() []*Student {
o := orm.NewOrm() //产生一个orm对象
o.Using("default") //这句话的意思是使用定义的默认数据库,与main.go中的orm.RegisterDataBase()对应
var students []*Student //定义指向结构体数组的指针
q := o.QueryTable("student")//获得一个数据库表的请求
q.All(&students)//取到这个表中的所有数据

return students

}


//该函数根据student中的Id,返回该学生的信息
func GetStudentById(id int) Student {
u := Student{Id:id}//根据所传入的Id得到对应student的对象
o := orm.NewOrm()//new 一个orm对象
o.Using("default")//使用最开始定义的default数据库
err := o.Read(&u)//读取Id=id的student的信息

if err == orm.ErrNoRows {
fmt.Println("查询不到")//对应操作,不一定是print
} else if err == orm.ErrMissPK {
fmt.Println("没有主键")
}

return u
}


//添加一个学生的信息到数据库中,参数是指向student结构题的指针
func AddStudent(student *Student) Student {
o := orm.NewOrm()
o.Using("default")
o.Insert(student)//插入数据库

return *student
}

func UpdateStudent(student *Student) {
o := orm.NewOrm()
o.Using("default")
o.Update(student)//更新该student的信息
}

func DeleteStudent(id int) {
o := orm.NewOrm()
o.Using("default")
o.Delete(&Student{Id:id})//删除对应id的student的信息
}

func init() {
orm.RegisterModel(new(Student))//将数据库注册到orm
}
  • model这一层主要是定义struct,并为上层编写读写数据库。处理数据的代码。

controller层实现

基于 beego 的 Controller 设计,只需要匿名组合 beego.Controller 就可以了,如下所示:

1
2
3
type xxxController struct {
beego.Controller
}

beego.Controller 实现了接口 beego.ControllerInterfacebeego.ControllerInterface 定义了如下函数:

  • Init(ct *context.Context, childName string, app interface{})

    这个函数主要初始化了 Context、相应的 Controller 名称,模板名,初始化模板参数的容器 Data,app 即为当前执行的 Controller 的 reflecttype,这个 app 可以用来执行子类的方法。

  • Prepare()

    这个函数主要是为了用户扩展用的,这个函数会在下面定义的这些 Method 方法之前执行,用户可以重写这个函数实现类似用户验证之类。

  • Get()

    如果用户请求的 HTTP Method 是 GET,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Get 请求。

  • Post()

    如果用户请求的 HTTP Method 是 POST,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Post 请求。

  • Delete()

    如果用户请求的 HTTP Method 是 DELETE,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Delete 请求。

  • Put()

    如果用户请求的 HTTP Method 是 PUT,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Put 请求.

  • Head()

    如果用户请求的 HTTP Method 是 HEAD,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Head 请求。

  • Patch()

    如果用户请求的 HTTP Method 是 PATCH,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Patch 请求.

  • Options()

    如果用户请求的HTTP Method是OPTIONS,那么就执行该函数,默认是 405,用户继承的子 struct 中可以实现了该方法以处理 Options 请求。

  • Finish()

    这个函数是在执行完相应的 HTTP Method 方法之后执行的,默认是空,用户可以在子 struct 中重写这个函数,执行例如数据库关闭,清理数据之类的工作。

  • Render() error

    这个函数主要用来实现渲染模板,如果 beego.AutoRender 为 true 的情况下才会执行。

所以通过子 struct 的方法重写,用户就可以实现自己的逻辑。

routers层实现

什么是路由设置呢?前面介绍的 MVC 结构执行时,介绍过 beego 存在三种方式的路由:固定路由、正则路由、自动路由,与RESTFul API相关的就是固定路由和正则路由。

下面就是固定路由的例子

1
2
3
4
beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &admin.UserController{})
beego.Router("/admin/index", &admin.ArticleController{})
beego.Router("/admin/addpkg", &admin.AddController{})

下面是正则路由的例子:

  • beego.Router(“/api/?:id”, &controllers.RController{})

    默认匹配 //例如对于URL”/api/123”可以匹配成功,此时变量”:id”值为”123”

  • beego.Router(“/api/:id”, &controllers.RController{})

    默认匹配 //例如对于URL”/api/123”可以匹配成功,此时变量”:id”值为”123”,但URL”/api/“匹配失败

  • beego.Router(“/api/:id([0-9]+)“, &controllers.RController{})

    自定义正则匹配 //例如对于URL”/api/123”可以匹配成功,此时变量”:id”值为”123”

  • beego.Router(“/user/:username([\w]+)“, &controllers.RController{})

    正则字符串匹配 //例如对于URL”/user/astaxie”可以匹配成功,此时变量”:username”值为”astaxie”

  • beego.Router(“/download/.”, &controllers.RController{})

    *匹配方式 //例如对于URL”/download/file/api.xml”可以匹配成功,此时变量”:path”值为”file/api”, “:ext”值为”xml”

  • beego.Router(“/download/ceshi/*“, &controllers.RController{})

    *全匹配方式 //例如对于URL”/download/ceshi/file/api.json”可以匹配成功,此时变量”:splat”值为”file/api.json”

  • beego.Router(“/:id:int”, &controllers.RController{})

    int 类型设置方式,匹配 :id为int 类型,框架帮你实现了正则 ([0-9]+)

  • beego.Router(“/:hi:string”, &controllers.RController{})

    string 类型设置方式,匹配 :hi 为 string 类型。框架帮你实现了正则 ([\w]+)

  • beego.Router(“/cms_:id([0-9]+).html”, &controllers.CmsController{})

    带有前缀的自定义正则 //匹配 :id 为正则类型。匹配 cms_123.html 这样的 url :id = 123

个人觉得,最方便的还是类似于Python框架flask的注解路由,也是在这个项目中使用的:

  • 在routers/routers.go里面添加你所希望的API

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    package routers

    import (
    "firstAPI/controllers"

    "github.com/astaxie/beego"
    )

    func init() {
    ns := beego.NewNamespace("/v1",
    beego.NSNamespace("/object",
    beego.NSInclude(
    &controllers.ObjectController{},
    ),
    ),
    beego.NSNamespace("/user",
    beego.NSInclude(
    &controllers.UserController{},
    ),
    ),
    beego.NSNamespace("/student",
    beego.NSInclude(
    &controllers.StudentController{},
    ),
    ),
    )
    beego.AddNamespace(ns)
    }

以上代码实现了如下的API:

/v1/object

/v1/user

/v1/student

非常清晰明了。

main.go的数据库配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
_ "firstAPI/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
_ "github.com/go-sql-driver/mysql"
)
func init() {
orm.RegisterDriver("mysql", orm.DRMySQL)//注册MySQL的driver
orm.RegisterDataBase("default", "mysql", "root:test@tcp(127.0.0.1:3306)/restapi_test?charset=utf8")//本地数据库的账号。密码等
orm.RunSyncdb("default", false, true)

}
func main() {

if beego.BConfig.RunMode == "dev" {
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"//静态文档
}

beego.Run()
}

关键点都在代码中以注释的形式展现。

postman测试

bee run 运行代码后,我们使用postman测试一下我们所构建的API效果如何。

这里节省篇幅,只测试一个接口。

到此为止,我们基于beego就实现了简单API接口的构建,是不是既清晰又简单呢?赶快自己动手试试吧!

本期技术周刊结束,代码已上传到GitHub,可以查阅,我们下期再会!


-------------The End-------------

本文标题:技术周刊之基于beego web框架的RESTful API的构建之旅

文章作者:cloud sjhan

发布时间:2018年10月14日 - 15:10

最后更新:2018年10月14日 - 18:10

原始链接:https://cloudsjhan.github.io/2018/10/14/技术周刊之基于beego-web框架的RESTful-API的构建之旅/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

cloud sjhan wechat
subscribe to my blog by scanning my public wechat account
坚持原创技术分享,您的支持将鼓励我继续创作!
0%
;