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 相应目录下生成如下目录结构的项目:

beego restful api 定义 beego使用_后端

new这个命令生成的一般是前后端没分离的项目。

api命令

上面的 new 命令是用来新建 Web 项目,不过很多用户使用 beego 来开发 API 应用。所以这个 api 命令就是用来创建 API 应用的,执行命令之后如下所示:

beego restful api 定义 beego使用_beego_02

从上面的目录我们可以看到和 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

如下图:

beego restful api 定义 beego使用_bee_03

访问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 restful api 定义 beego使用_golang_04

beego.BConfig.WebConfig.DirectoryIndex 属于web配置部分,DirectoryIndex 表示 是否开启静态目录的列表显示,默认不显示目录(为false),返回 403 错误。

beego.BConfig.WebConfig.StaticDir 属于web配置部分,StaticDir 是静态文件目录设置,默认是static,此处设置为swagger,就可以访问到swagger/index.html。使用swagger工具。如下图:

beego restful api 定义 beego使用_开发语言_05

在这个index.html中,需要把第42行的url进行修改,如下图:

beego restful api 定义 beego使用_golang_06

>修改原因:…

最后是我们的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一类的。