基于beego构建Restful API服务
听闻Go号称“21世纪的C语言”,又比较适合做Web后端的应用,加之本人对C/C++比较熟悉,计划通过构建一个提供Restful API的服务来达到学习的目的。
github上浏览了一下相关的框架挺多,如:hugo、gin、beego、echo、iris等,还有mux(A powerful URL router and dispatcher)看上去也颇为合适。由于本人Web开发(java/php/node.js什么的都不会)小白,打算先从beego入手。
一、创建API项目并运行
1. beego的项目可以通过bee工具来创建,RESTful的项目通过api参数来创建。创建imc项目命令:
bee api imc
如此会在$GOPATH/src目录下生成一个imc目录。
2. 运行imc项目,启动swagger便于测试。
先进入到$GOPATH/src/imc目录下,执行以下命令:
bee run -gendoc=true -downdoc=true
如此便启动了API自动化文档,以及会自动下载swagger。浏览器直接访问http://localhost:8080/swagger/即可进行测试。
-gendoc=true表示每次会自动化的build文档,-downdoc=true表示会自动下载swagger文档查看器。
二、自动生成文件(代码)分析
1. imc/conf/app.conf
app.conf是beego的默认配置文件。
appname = imc httpport = 8080 runmode = dev autorender = false copyrequestbody = true EnableDocs = true
appname: 应用名称,默认是beego。通过bee new或bee api创建,则是项目名称。beego.BConfig.AppName = "imc"。
httpport:应用监听端口,默认是8080。beego.BConfig.Listen.HTTPPort = 8080。
runmode:应用的运行模式,可选值为prod、dev或test。默认是dev开发模式。beego.BConfig.RunMode = "dev"。
autorender:是否模板自动渲染,默认值为true,对于API类型的应用,需要把该项设为false。beego.BConfig.WebConfig.AutoRender = true。
copyrequestbody:是否允许在HTTP请求时,返回原始请求体数据字节,默认为false(GET or HEAD or 上传文件请求除外)。beego.BConfig.CopyRequestBody = false。
EnableDocs:是否开启文档内置功能,默认为false。beego.BConfig.WebConfig.EnableDocs = true。
2. main.go
package main import ( _ "deepspirit/imc/routers" "github.com/astaxie/beego" ) func main() { if beego.BConfig.RunMode == "dev" { beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" } beego.Run() }
程序入口,通过beego.Run()将beego的框架运行起来。当以dev开发者模式启动时,设置一些参数。(暂时不用关注)
import中的_ "deepspirit/imc/routers"语句目的是执行routers包中的init函数。Go允许一个package中多个文件包含init函数,执行顺序为文件名字母顺序。
Go语言的代码执行过程如下图:
3. imc/routers/*.go
commentsRouter_controller.go:根据router.go和controllers/*.go自动生成。从beego 1.3版本开始支持注解路由功能,用户无需在router中注册路由,只需要Include相应的controller,然后在controller的method方法上面写上router注释(// @router)即可。
router.go:
package routers import ( "deepspirit/imc/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.AddNamespace(ns) }
添加/v1/object/对应ObjectController中的注解路由。
添加/v1/user/对应UserControoler中的注解路由。
4. imc/controllers/*.go
object.go:
package controllers import ( "deepspirit/imc/models" "encoding/json" "github.com/astaxie/beego" ) // 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 (o *ObjectController) Post() { var ob models.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) objectid := models.AddOne(ob) o.Data["json"] = map[string]string{"ObjectId": objectid} o.ServeJSON() } // @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 (o *ObjectController) Get() { objectId := o.Ctx.Input.Param(":objectId") if objectId != "" { ob, err := models.GetOne(objectId) if err != nil { o.Data["json"] = err.Error() } else { o.Data["json"] = ob } } o.ServeJSON() } // @Title GetAll // @Description get all objects // @Success 200 {object} models.Object // @Failure 403 :objectId is empty // @router / [get] func (o *ObjectController) GetAll() { obs := models.GetAll() o.Data["json"] = obs o.ServeJSON() } // @Title Update // @Description update the object // @Param objectId path string true "The objectid you want to update" // @Param body body models.Object true "The body" // @Success 200 {object} models.Object // @Failure 403 :objectId is empty // @router /:objectId [put] func (o *ObjectController) Put() { objectId := o.Ctx.Input.Param(":objectId") var ob models.Object json.Unmarshal(o.Ctx.Input.RequestBody, &ob) err := models.Update(objectId, ob.Score) if err != nil { o.Data["json"] = err.Error() } else { o.Data["json"] = "update success!" } o.ServeJSON() } // @Title Delete // @Description delete the object // @Param objectId path string true "The objectId you want to delete" // @Success 200 {string} delete success! // @Failure 403 objectId is empty // @router /:objectId [delete] func (o *ObjectController) Delete() { objectId := o.Ctx.Input.Param(":objectId") models.Delete(objectId) o.Data["json"] = "delete success!" o.ServeJSON() }
// @router / [post]
该注解路由表示支持/v1/object/的POST方法(此处url为/,结合router.go中的/v1/object)
// @router /:objectId [get]
该注解路由表示支持/v1/object/{objectId}的GET方法
// @router / [get]
该注解路由表示支持/v1/object的GET方法
// @router /:objectId [put]
该注解路由表示支持/v1/object/{objectId}的PUT方法
// @router /:objectId [delete]
该注解路由表示支持/v1/object/{objectId}的DELETE方法
user.go:
package controllers import ( "deepspirit/imc/models" "encoding/json" "github.com/astaxie/beego" ) // 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() } // @Title Get // @Description get user by uid // @Param uid path string true "The key for staticblock" // @Success 200 {object} models.User // @Failure 403 :uid is empty // @router /:uid [get] func (u *UserController) Get() { uid := u.GetString(":uid") if uid != "" { user, err := models.GetUser(uid) if err != nil { u.Data["json"] = err.Error() } else { u.Data["json"] = user } } u.ServeJSON() } // @Title Update // @Description update the user // @Param uid path string true "The uid you want to update" // @Param body body models.User true "body for user content" // @Success 200 {object} models.User // @Failure 403 :uid is not int // @router /:uid [put] func (u *UserController) Put() { uid := u.GetString(":uid") if uid != "" { var user models.User json.Unmarshal(u.Ctx.Input.RequestBody, &user) uu, err := models.UpdateUser(uid, &user) if err != nil { u.Data["json"] = err.Error() } else { u.Data["json"] = uu } } u.ServeJSON() } // @Title Delete // @Description delete the user // @Param uid path string true "The uid you want to delete" // @Success 200 {string} delete success! // @Failure 403 uid is empty // @router /:uid [delete] func (u *UserController) Delete() { uid := u.GetString(":uid") models.DeleteUser(uid) u.Data["json"] = "delete success!" u.ServeJSON() } // @Title Login // @Description Logs user into the system // @Param username query string true "The username for login" // @Param password query string true "The password for login" // @Success 200 {string} login success // @Failure 403 user not exist // @router /login [get] func (u *UserController) Login() { username := u.GetString("username") password := u.GetString("password") if models.Login(username, password) { u.Data["json"] = "login success" } else { u.Data["json"] = "user not exist" } u.ServeJSON() } // @Title logout // @Description Logs out current logged in user session // @Success 200 {string} logout success // @router /logout [get] func (u *UserController) Logout() { u.Data["json"] = "logout success" u.ServeJSON() }
// @router / [post]
该注解路由表示支持/v1/user/的POST方法
// @router / [get]
该注解路由表示支持/v1/user/的GET方法
// @router /:uid [get]
该注解路由表示支持/v1/user/{uid}的GET方法
// @router /:uid [put]
该注解路由表示支持/v1/user/{uid}的PUT方法
// @router /:uid [delete]
该注解路由表示支持/v1/user/{uid}的DELETE方法
// @router /login [get]
该注解路由表示支持/v1/user/login的GET方法
// @router /logout [get]
该注解路由表示支持/v1/user/logout的GET方法
5. imc/models/*.go
object.go和user.go,代码不再罗列。model层主要数据与数据库相关的操作,自动生成的例子不含数据库操作,后续ORM的部分放在该目录下。
三、ORM使用
1. 安装MySQL 8
2. 下载Go语言的驱动
go get github.com/go-sql-driver/mysql
3. 使用orm访问数据库
func init() { // set default database orm.RegisterDataBase("default", "mysql", "username:password@tcp(127.0.0.1:3306)/db_name?charset=utf8", 30) // register model orm.RegisterModel(new(models.Role)) // create table orm.RunSyncdb("default", false, true) }
注册模型之后,调用RunSyncdb,若对应模型的表不存在会自动创建。以上代码考虑放在main.go中。
向models目录中添加role.go,代码如下: