素材巴巴 > 程序开发 >

Beego API 快速入门

程序开发 2023-09-20 21:09:25

更多分享内容可访问我的个人博客

https://www.niuiic.top/

本文将通过一个简单的案例帮助初学者打通 beego api 开发流程。

环境配置

  1. 安装 golang,配置GOPROXY, GOPATH, GOROOT,启用 go module。

  2. 在 shell 配置文件中加入export GOPATH="yourPath"。并且将 GOPATH 中的 bin 目录加入 PATH。

  3. 安装 bee。go install github.com/beego/bee/v2@latest

  4. 创建新项目。

bee api quickstart
 go mod tidy
 bee generate docs
 

项目结构与执行逻辑

beego 是一个典型的 MVC 架构。它的执行逻辑如下图所示。

在这里插入图片描述

当前创建的是 api 项目,没有前端部分。

相对应的项目目录如下所示。

在这里插入图片描述

运行项目

在项目目录下使用bee run -gendoc=true -downdoc=true运行项目。第一次运行时会自动下载调试工具swagger

访问http://127.0.0.1:8080/swagger/可以看到调试界面。

源码分析

Entry

首先来看入口文件main.go

package mainimport (_ "quickstart/routers"beego "github.com/beego/beego/v2/server/web"
 )func main() {if beego.BConfig.RunMode == "dev" {beego.BConfig.WebConfig.DirectoryIndex = truebeego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"}beego.Run()
 }
 

main函数中第一条语句是设置在 dev 运行模式下启动 swagger 调试器,这与项目主干无关,暂时不管它。

第二条语句调用了一个Run方法,运行整个程序。那么到底是怎么运行的呢,看到一个关键的引入_ "quickstart/routers"。这个包只引入了其中的 init 函数,我们去到routers/router.go查看到底干了什么。

Routers

package routersimport ("quickstart/controllers"beego "github.com/beego/beego/v2/server/web"
 )func init() {ns := beego.NewNamespace("/v1",beego.NSNamespace("/object",beego.NSInclude(&controllers.ObjectController{},),),beego.NSNamespace("/user",beego.NSInclude(&controllers.UserController{},),),)beego.AddNamespace(ns)
 }
 

以上init函数做的工作,简单地说,就是将不同的请求对应到不同的控制器。

本目录中还有另一个文件commentsRouter_controllers.go。以下列出部分内容。

package routersimport (beego "github.com/beego/beego/v2/server/web""github.com/beego/beego/v2/server/web/context/param"
 )func init() {beego.GlobalControllerRouter["quickstart/controllers:UserController"] = append(beego.GlobalControllerRouter["quickstart/controllers:UserController"],beego.ControllerComments{Method: "Get",Router: "/:uid",AllowHTTPMethods: []string{"get"},MethodParams: param.Make(),Filters: nil,Params: nil})}
 

看到quickstart/controllers:UserController,这其实对应了 controllers 这一层的一个控制器。再看到Method: "Get",这是该控制器实现的一个方法。所以这个文件可以看作是注册控制器与函数。其余的配置暂时不管,重点关注Router: "/:uid"。这个配置的意思是当有 Get 请求http://127.0.0.1:8080/v1/user/xxx(可以直接用浏览器访问该地址)时,会执行控制器中的Get函数。

xxx 可以任意但是必须有,这里写的 :uid 是在实现控制器方法的时候读取输入用的,不是指 xxx 必须是 :uid。

那么,至此便知道如何用不同的请求执行不同的函数了。

然后再去到 controllers/user.go,查看这一层干了什么。

Controllers

package controllersimport ("quickstart/models""encoding/json"beego "github.com/beego/beego/v2/server/web"
 )// 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.Userjson.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"] = usersu.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.Userjson.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()
 }
 

首先定义了UserController,该结构体获取了beego.Controller的所有方法。后面的所有内容则都是在重写这些方法。观察方法的名称可以轻易地发现,这些方法对应不同类型的 http 请求。

方法内使用的结构体及其方法来自models/user.go。于是,再看models/user.go

Models

package modelsimport ("errors""strconv""time"
 )var (UserList map[string]*User
 )func init() {UserList = make(map[string]*User)u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}}UserList["user_11111"] = &u
 }type User struct {Id       stringUsername stringPassword stringProfile  Profile
 }type Profile struct {Gender  stringAge     intAddress stringEmail   string
 }func AddUser(u User) string {u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10)UserList[u.Id] = &ureturn u.Id
 }func GetUser(uid string) (u *User, err error) {if u, ok := UserList[uid]; ok {return u, nil}return nil, errors.New("User not exists")
 }func GetAllUsers() map[string]*User {return UserList
 }func UpdateUser(uid string, uu *User) (a *User, err error) {if u, ok := UserList[uid]; ok {if uu.Username != "" {u.Username = uu.Username}if uu.Password != "" {u.Password = uu.Password}if uu.Profile.Age != 0 {u.Profile.Age = uu.Profile.Age}if uu.Profile.Address != "" {u.Profile.Address = uu.Profile.Address}if uu.Profile.Gender != "" {u.Profile.Gender = uu.Profile.Gender}if uu.Profile.Email != "" {u.Profile.Email = uu.Profile.Email}return u, nil}return nil, errors.New("User Not Exist")
 }func Login(username, password string) bool {for _, u := range UserList {if u.Username == username && u.Password == password {return true}}return false
 }func DeleteUser(uid string) {delete(UserList, uid)
 }
 

流程总结

至此,虽然还没有完整分析所有文件,但项目的主体逻辑已经明了。项目运行之后,若接收到 http 请求,则首先通过 router 到对应的 controller,在 controller 中完成所有操作。如果操作较为复杂,则可以编写 model,增加层数,提高复用性。

案例:用户注册与查询

预备技能

与数据库交互

本案例用到数据库,请自行安装并配置 mysql 数据库。案例使用"github.com/beego/beego/v2/client/orm""github.com/go-sql-driver/mysql"实现数据库交互。mysql driver 需要单独安装,go get github.com/go-sql-driver/mysql

先新建一个项目。

mkdir database
 cd database
 go mod init database
 go get github.com/beego/beego/v2/client/orm
 go get github.com/go-sql-driver/mysql
 

编写main.go

package mainimport ("github.com/beego/beego/v2/client/orm"_ "github.com/go-sql-driver/mysql"
 )type User struct {ID       int    `orm:"column(id)"`Name     string `orm:"column(name)"`Password string `orm:"column(password)"`
 }func init() {// register modelorm.RegisterModel(new(User))// register default database// modify username, password and database for yourselform.RegisterDataBase("default", "mysql", "username:password@tcp(127.0.0.1:3306)/database?charset=utf8")
 }func main() {orm.Debug = true// automatically build tableorm.RunSyncdb("default", false, true)// create orm objecto := orm.NewOrm()// datauser := new(User)user.Name = "mike"// insert datao.Insert(user)
 }
 

运行go run main.go并查看 mysql 中的数据。发现新建了一个表并且填入了数据。

这是一个很简单的案例,其余操作可以参考相关文档。

测试

纯粹的测试很简单,可以参考该系列文章。

现在将上面的代码稍微修改一下,写入models/user.go

package modelsimport ("github.com/beego/beego/v2/client/orm"_ "github.com/go-sql-driver/mysql"
 )type User struct {ID       int    `orm:"column(id)"`Name     string `orm:"column(name)"`Password string `orm:"column(password)"`
 }func Execute() {// set to debug modeorm.Debug = true// register model and databaseorm.RegisterModel(new(User))orm.RegisterDataBase("default", "mysql", "username:password@tcp(127.0.0.1:3306)/database?charset=utf8")// automatically build tableorm.RunSyncdb("default", false, true)// create orm objecto := orm.NewOrm()// datauser := new(User)user.Name = "mike"// insert datao.Insert(user)
 }
 

然后编写tests/models_user_test.go文件。

package testsimport ("quickstart/models""testing"
 )func TestExecute(t *testing.T) {models.Execute()
 }
 

使用go test ./tests即可完成测试。当然这只是最简单的写法,要进行更精确友好的测试可以自行添加代码。

案例

为了方便起见,我们参考原有的程序,从下往上写。

Models

config.go是读取配置的文件,自行在项目根目录下建立一个 config.json 文件,并且把对应字段写好即可(sql_user等)。

package modelsimport ("encoding/json""fmt""io/ioutil""os"
 )type Config struct {SqlUser     string `json:"sql_user"`SqlPassword string `json:"sql_password"`SqlDatabase string `json:"sql_database"`SqlPort     int `json:"sql_port"`
 }func ReadConfig(configPath string) Config {configFile, err := os.Open(configPath)if err != nil {fmt.Println(err)}defer configFile.Close()byteValue, _ := ioutil.ReadAll(configFile)var config Configjson.Unmarshal([]byte(byteValue), &config)return config
 }
 

user.go定义了 user 这个表的模型。

package modelstype User struct {ID       uint   `orm:"column(id);auto"`Name     string `orm:"column(name);unique;size(20)"`Password string `orm:"column(password);size(16)"`
 }
 

operation.go定义了针对数据库的一系列操作。其中orm.Ormer已在 v2 版本中修改,文档建议设置成全局变量,这里在外面包了一层加强安全性。另外因为只是一个简单的案例,方法只是简单地写了几个。

package modelsimport ("fmt""strconv""github.com/beego/beego/v2/client/orm"_ "github.com/go-sql-driver/mysql"
 )type DBHandle struct {orm orm.Ormer
 }var DBH DBHandlefunc init() {// read configconfig := ReadConfig("./config.json")// set to debug modeorm.Debug = true// register model and databaseorm.RegisterModel(new(User))databaseUrl := config.SqlUser + ":" + config.SqlPassword + "@tcp(127.0.0.1:" +strconv.Itoa(config.SqlPort) + ")/" + config.SqlDatabase + "?charset=utf8"orm.RegisterDataBase("default", "mysql", databaseUrl)// automatically build tableorm.RunSyncdb("default", false, true)// create orm object with specified database as recommended in the documentDBH.orm = orm.NewOrmUsingDB("default")
 }// @Title Insert
 // @Description insert new data
 // @Param mode: must be a pointer
 func (this *DBHandle) Insert(mode interface{}) {_, err := this.orm.Insert(mode)if err != nil {fmt.Println(err)}
 }// @Title Delete
 // @Description delete by primary key
 // @Param mode: must be a pointer
 func (this *DBHandle) Delete(mode interface{}) {_, err := this.orm.Delete(mode)if err != nil {fmt.Println(err)}
 }// @Title Update
 // @Description update by primary key
 // @Param mode: must be a pointer
 func (this *DBHandle) Update(mode interface{}) {_, err := this.orm.Update(mode)if err != nil {fmt.Println(err)}
 }// @Title QueryByPK
 // @Description query by primary key
 //				remenber to specify the primary key for mode before pass it
 // @Param mode: must be a pointer
 func (this *DBHandle) QueryByPK(mode interface{}) {err := this.orm.Read(mode)if err != nil {fmt.Println(err)}
 }// @Title QueryByField
 // @Description query by field
 // @Param mode: must be a pointer
 func (this *DBHandle) QueryByField(mode interface{}, table string, field string,value interface{}) {_, err := this.orm.QueryTable(table).Filter(field, value).All(mode)if err != nil {fmt.Println(err)}
 }
 

Controllers

user.go定义了UserController这个控制器。

package controllersimport ("quickstart/models""strconv"beego "github.com/beego/beego/v2/server/web"
 )type UserController struct {beego.Controller
 }func (u *UserController) Get() {uid := u.GetString(":uid")user := models.User{}if uid == "create" {user.Name = "Bob"user.Password = "asdj"models.DBH.Insert(&user)u.Data["json"] = "succeed to add a user"} else if uid != "" {id, _ := strconv.Atoi(uid)user.ID = uint(id)models.DBH.QueryByPK(&user)u.Data["json"] = user.Name}u.ServeJSON()
 }
 

Routers

router.go只需要 user 的部分。

package routersimport ("quickstart/controllers"beego "github.com/beego/beego/v2/server/web"
 )func init() {ns := beego.NewNamespace("/v1",beego.NSNamespace("/user",beego.NSInclude(&controllers.UserController{},),),)beego.AddNamespace(ns)
 }
 

commentsRouter_controllers.go只留下 user 的 get 函数。

package routersimport (beego "github.com/beego/beego/v2/server/web""github.com/beego/beego/v2/server/web/context/param"
 )func init() {beego.GlobalControllerRouter["quickstart/controllers:UserController"] = append(beego.GlobalControllerRouter["quickstart/controllers:UserController"],beego.ControllerComments{Method: "Get",Router: "/:uid",AllowHTTPMethods: []string{"get"},MethodParams: param.Make(),Filters: nil,Params: nil})}
 

测试

在浏览器中访问127.0.0.1:8080/v1/user/create可以创建一个用户,然后看到创建成功的信息。

再访问127.0.0.1:8080/v1/user/1就可以查询到 id 为 1 的用户的名称。

启用 https 协议

为什么需要 https,一句话,只有 http 寸步难行。

这一部分的解决方案在网上随便搜一下就有,这里再写一下,省的继续查。

实现的方法很简单,首先需要一个 SSL 证书。自己做一个或者买一个都可以。不用问怎么选,买的显然更好,自己做的只能用于开发环境。

下面介绍一下怎么自制一个 SSL 证书。因为只是用在开发环境,所以搞得简单点就行了。

# 生成服务器私钥
 openssl genrsa -out server.key 1024
 # 根据私钥和输入的信息生成证书请求文件,相关信息知道的就填,不清楚的随便填
 openssl req -new -key server.key -out server.csr
 # 用第一步的私钥和第二步的请求文件生成证书
 openssl x509 -req -in server.csr -out server.crt -signkey server.key -days 3650
 

然后把server.keyserver.crt放到 beego api 项目的 conf 目录下,并且修改app.conf如下。

EnableHTTPS = true
 EnableHttpTLS = true
 EnableHttp = false
 HttpsPort = 8000
 HTTPSCertFile = "conf/server.crt"
 HTTPSKeyFile = "conf/server.key"
 

最好把 http 关了,防止可能出现的一系列问题。

现在就只能通过https://……进行请求了,注意由于使用自己制作的 SSL 证书,浏览器一定会报安全问题,不用管它就行。如果客户端不是浏览器,那必须在客户端信任自己生成的证书或者关闭证书校验,否则两边是连不上的。

另外,将项目打包之后可执行文件同一级目录下必须要有一个 conf 目录,且其中包含这两个证书文件。


标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。