beego快速上手使用
- 安装beego和bee
- bee工具命令
- new命令
- api命令
- generate命令
- 项目初始构建
- 运行项目
- 源码分析
- main.go
- routers.go
- Controllers/user.go
- models/user.go
安装beego和bee
beego
go get -u github.com/beego/beego/v2
bee
go get -u github.com/beego/bee/v2
安装完之后,
bee
可执行文件默认存放在$GOPATH/bin
里面,所以你需要把$GOPATH/bin
添加到你的环境变量中,才可以进行下一步。如果发现
$GOPATH/bin
里面没有bee.exe,那就去下载的bee依赖包中(一般是pkg/mod/github.com/beego/bee)去使用go build -o bee.exe执行生成bee.exe,然后把这个exe放到$GOPATH/bin
里面,然后添加到环境变量。
bee工具命令
new命令
new
命令是新建一个 Web 项目,我们在命令行下执行 bee new <项目名>
就可以创建一个新的项目。但是注意该命令必须在 $GOPATH/src
下执行。最后会在 $GOPATH/src
相应目录下生成如下目录结构的项目:
new这个命令生成的一般是前后端没分离的项目。
api命令
上面的 new
命令是用来新建 Web 项目,不过很多用户使用 beego 来开发 API 应用。所以这个 api
命令就是用来创建 API 应用的,执行命令之后如下所示:
从上面的目录我们可以看到和 Web 项目相比,少了 static 和 views 目录,多了一个 test 模块,用来做单元测试的。
api命令一般生成前后端分离的项目
generate命令
这个命令是用来自动化的生成代码的,包含了从数据库一键生成 model,还包含了 scaffold 的,通过这个命令,让大家开发代码不再慢
例如:bee generate docs
bee generate docs
此命令含义generate swagger doc file 生成swagger文档文件
项目初始构建
bee api quickstart
go mod tidy
bee generate docs
注意执行目录
运行项目
在项目目录下使用bee run -gendoc=true -downdoc=true
运行项目。第一次运行时会自动下载调试工具swagger
。
-gendoc=true
表示每次自动化的 build 文档。当项目文件中controller或者routers目录下有变动时,就需要添加这一句
-downdoc=true
会自动的下载 swagger 文档查看器。仅需要在第一次运行时使用,他会自动下载调试工具swagger
如下图:
访问http://127.0.0.1:8080/swagger/
可以看到调试界面。
源码分析
在使用 bee 工具快速构建出项目后,我们来看看他生成的代码,对代码进行一个初步的分析。
main.go
首先看入口文件,main.go
package main
import (
_ "quickstart/routers"
beego "github.com/beego/beego/v2/server/web"
)
func main() {
if beego.BConfig.RunMode == "dev" {
beego.BConfig.WebConfig.DirectoryIndex = true
beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
}
beego.Run()
}
main函数中首先进行配置设置(if语句中的内容),然后beego.Run启动服务。
BConfig就是beego里面默认的配置。
beego.BConfig.RunMode
是 应用的运行模式。在conf文件中我们可以看到beego默认配置的运行模式,如下:
beego.BConfig.WebConfig.DirectoryIndex
属于web配置部分,DirectoryIndex 表示 是否开启静态目录的列表显示,默认不显示目录(为false),返回 403 错误。
beego.BConfig.WebConfig.StaticDir
属于web配置部分,StaticDir 是静态文件目录设置,默认是static,此处设置为swagger,就可以访问到swagger/index.html。使用swagger工具。如下图:
在这个index.html中,需要把第42行的url进行修改,如下图:
>修改原因:…
最后是我们的beego.Run()方法,使用它来运行整个程序。如何运行呢?从import可以找到一个引入 _ "quickstart/routers"
,这个包只引入了init函数,所以接下去就去routers/router.go
查看
routers.go
// @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
package routers
import (
"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)
}
routers.go中只有一个init函数,这个函数简单来说就是 把不同的请求对应到不同的控制器。请求分发。
具体分析:
beego.NewNamespace
是创建命名空间
beego.NSNamespace
是希望namespace
内部嵌套namespace
时使用,NSNamespace
来注入一个子namespace
。
beego.NSInclude
该方法是注解路由的配套方法,只对注解路由生效。
最后使用beego.AddNamespace(ns)
来注册命名空间。
beego的命名空间就和 gin的路由组是差不多的
在routers.go目录中还有另一个文件commentsRouter_controllers.go
部分代码如下:
package routers
import (
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
就对应了一层controller。
每个controller中更加具体的方法体现在 Method上,Method: "Get"
,这是该控制器实现的一个方法。
这个文件就可以看作是注册控制器与函数
这个文件是通过注解路由自动生成的,仅在dev模式下生成
Router: "/:uid"
。这个配置的意思是当有 Get 请求http://127.0.0.1:8080/v1/user/xxx
(可以直接用浏览器访问该地址)时,会执行控制器中的Get
函数。
至此,就知道怎么使用不同的请求执行不同的函数。请求被分发到控制器下面的函数,所以下一步就是控制器了。
Controllers/user.go
package controllers
import (
"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.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()
}
首先定义UserController结构体,该结构体“继承”beego.Controller。后面的所有内容则都是在重写这些方法。观察方法的名称可以发现,这些方法对应不同类型的 http 请求,但是也有一些方法名和请求类型看不出关系的,例如 Login(),但是可以通过看上面注释内容,所以就得提到注解路由了!
注解路由:
swagger的配置就是通过注解实现的,这些注释中写的内容最终都将呈现在swagger工具上。
注解路由通过结构体方法上面的注释进行生成,生成的文件就是routers/commentsRouter_controllers.go
注意如果conf配置文件中的RunMode模式不再是dev,那么commentsRouter_controllers.go不会再自动生成和修改,那么访问就会出问题。所以需要先在dev下修改测试好,然后再切换到prod模式。
注释写法如下:
// @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]
我们可以把注释分类:
- @Title
接口的标题,用来标示唯一性,唯一,可选
格式:之后跟一个描述字符串 - @Description
接口的作用,用来描述接口的用途,唯一,可选
格式:之后跟一个描述字符串 - @Param
请求的参数,用来描述接受的参数,多个,可选
格式:变量名 传输类型 类型 是否必须 描述
变量名:字符串
传输类型:path or body or query or form or header
- query 表示带在url串里面?aa=bb&cc=dd
- form 表示使用表单递交数据
- path 表示URL串中得字符,例如/user/{uid} 那么uid就是一个path类型的参数
- body 表示使用raw body进行数据的传输
- header 表示通过header进行数据的传输
类型:
- string
- int
- int64
- 对象,这个地方大家写的时候需要注意,需要是相对于当前项目的路径.对象,例如
models.Object
表示models
目录下的Object对象,这样bee在生成文档的时候会去扫描改对象并显示给用户改对象。
是否必须:true 或者false
描述:字符串
- @Success
成功返回的code和对象或者信息
格式:code 对象类型 信息或者对象路径
code:表示HTTP的标准status code,200 201等
对象类型:{object}表示对象,其他默认都认为是字符类型,会显示第三个参数给用户,如果是{object}类型,那么就会去扫描改对象,并显示给用户
对象路径和上面Param中得对象类型一样,使用路径.对象的方式来描述 - @Failure
错误返回的信息,
格式: code 信息
code:同上Success
错误信息:字符串描述信息 - @router
上面已经描述过支持两个参数,第一个是路由,第二个表示支持的HTTP方法
方法内使用的结构体及其方法来自models/user.go。于是,再看models/user.go。
models/user.go
package models
import (
"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 string
Username string
Password string
Profile Profile
}
type Profile struct {
Gender string
Age int
Address string
Email string
}
func AddUser(u User) string {
u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10)
UserList[u.Id] = &u
return 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)
}
model层里面就是更加具体的函数操作。如CRUD一类的。