本章介绍Beego的控制器。

1. 参数配置

1.1 修改默认配置

beego默认会解析当前应用下的conf/app.conf文件,通过这个文件你可以初始化很多 beego 的默认参数:

appname = firstProject
httpport = 8080
runmode = dev

也可以在配置文件中配置应用需要用的一些配置信息,例如下面所示的数据库信息:

mysqlUser = "test"
mysqlPass = "asdfg12345"
mysqlIp ="cdb-axt937vt.gz.tencentcdb.com"
mysqlPort="10059"
mysqlDbname = "test"

可以通过如下的方式获取设置的配置信息:

m:=beego.AppConfig.String("mysqlUser")
n:=beego.AppConfig.String("mysqlPass")

1.2 不同级别的配置

根据不同的运行环境,产品的配置可能不同。

可以有不同的 Runmode 的配置,默认优先读取 runmode 下的配置信息,例如下面的配置文件,我们可以把配置分成三份,比如dev,prod,test:

appname = beegoDemo
runmode = dev   

[dev]
httpport = 9527
[test]
httpport = 5566
[prod] 
httpport = 8082

读取不同模式下配置参数的方法是“模式::配置参数名”,比如:

beego.AppConfig.String("dev::mysqluser")

2. 路由控制

路由指的就是一个url请求由谁来处理,我们知道Go的执行过程如下:

在Beego中默认路由的执行路径是:

routers包中,我们看到:

package routers

import (
    "beegoDemo/controllers"
    "github.com/astaxie/beego"
)

func init() {
    beego.Router("/", &controllers.MainController{})
}

路由初始化中,我们通过路由注册函数 beego.Router, 注册了一个MainController{}控制器。第一个参数是 URL (用户请求的地址),这里我们注册的是 /,也就是我们访问的不带任何参数的 URL。

在beego设计中,url请求可以由控制器的函数来处理,也可以由一个单独的函数来处理,因此路由设置由两部分组成:url路由处理函数。beego提供两种设置处理函数的方式:

  • 直接绑定一个函数
  • 绑定一个控制器对象 (RESTful方式)

2.1 处理函数方式

2.1.1 直接绑定函数

这种方式直接将一个url路由和一个函数绑定起来。可以用闭包来表示路由,所以可以在router里面直接写:

beego.Get("/hel",func(ctx *context.Context){
    ctx.Output.Body([]byte("hello world"))
})

这个函数借用了Beego提供的Get接口,但利用闭包重写了方法,输出hello world

此外还有基本的POST路由:

beego.Post("/alice",func(ctx *context.Context){
     ctx.Output.Body([]byte("bob"))
})

所有的支持的基础函数如下所示:

  • beego.Get(router, beego.FilterFunc)
  • beego.Post(router, beego.FilterFunc)
  • beego.Put(router, beego.FilterFunc)
  • beego.Head(router, beego.FilterFunc)
  • beego.Options(router, beego.FilterFunc)
  • beego.Delete(router, beego.FilterFunc)
  • beego.Any(router, beego.FilterFunc)

2.1.2 RESTful路由

在beego项目中,RESTful路由方式就是将url路由跟一个控制器对象绑定,然后Get请求由控制的Get函数处理,Post请求由Post函数处理,以此类推。

// url: / 的所有http请求方法都由MainController控制器的对应函数处理
beego.Router("/", &controllers.MainController{})

// url: /user 的所有http请求方法都由UserController控制器的对应函数处理
beego.Router("/user", &controllers.UserController{})

接下来看一个例子:

(1)在controller中写:

package controllers

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

type HelloController struct {
    beego.Controller 
}

func (hello *HelloController) Get() {
    hello.Ctx.WriteString("hello go")
}

这一步中我们声明了一个控制器 HelloController,内嵌了一个beego.Controller,类似于继承的效果。然后为控制器安排了一个方法Get(),输出hello go

(2)在routers的初始化函数中写:

beego.Router("/hello", &controllers.HelloController{})

/hello是URL的一部分,可以通过localhost:8080/hello访问。

2.3 路由方式

这一部分主要介绍url路由的规则,以下内容都适用于上面介绍的所有路由设置函数。

(1)固定路由

最常见的方式,固定路由指的是url规则是固定的一个url。

beego.Router("/user", &controllers.UserController{})
beego.Router("/shop/order", &controllers.OrderController{})
beego.Router("/shop/comment", &controllers.CommentController{})

(2)正则路由

由正则表达式形成的路由,先看一个例子:

首先在controller中定义一个控制器:

type RegExpController struct {
    beego.Controller
}
func (this *RegExpController) Get() {
    this.Ctx.WriteString(fmt.Sprintf("<p>In RegExp Mode</p>"))

    id := this.Ctx.Input.Param(":id")
    this.Ctx.WriteString(fmt.Sprintf("id is %s.<br />", id))
}

然后在router注册这个控制器,参数可以由:id传递:

// 正则路由从path中提取参数
beego.Router("/RegExp1/?:id", &controllers.RegExpController{})
// 正则表达式匹配数字,+表示匹配1次或多次
beego.Router("/RegExp2/?:id([0-9]+)", &controllers.RegExpController{})
//匹配下划线和字符
beego.Router("/RegExp3/?:id([\\w]+)", &controllers.RegExpController{})
// http://127.0.0.1:8081/RegExp4/abc123de  (id=123)
beego.Router("/RegExp4/abc:id([0-9]+)de", &controllers.RegExpController{})

键入不同的URL,会出现不同的结果:

键入:localhost:8080/RegExp1/12a
显示:
In RegExp Mode
id is 12a.

键入:localhost:8080/RegExp2/12a
显示:404

键入:localhost:8080/RegExp2/13
显示:
In RegExp Mode
id is 13

(3)自动路由

自动路由指的是通过反射获取到控制器的名字和控制器实现的所有函数名字,自动生成url路由。

假设我们编写了一个UserController控制器,为控制器添加了Login和Logout方法,正常来说,需要在router里面调用beego.Router两次,分别注册,这样非常麻烦

如果采用自动路由:

beego.AutoRouter(&controllers.UserController{})

url自动路由例子:

/user/login   调用 UserController 中的 Login 方法
/user/logout  调用 UserController 中的 Logout 方法

3. 获取请求参数

我们经常需要获取用户传递的数据,包括 Get、POST 等方式的请求,beego里面会自动解析这些数据。

3.1 默认获取参数的方式

beego.Controller基础控制器为我们提供了GetXXX序列获取参数的函数, XXX指的就是返回不同的数据类型。

type HelloController struct {
    beego.Controller 
}

func (this *HelloController) Get(){
    id,_:=this.GetInt("id")
    username:=this.GetString("username","none")

    this.Ctx.WriteString(strconv.Itoa(id))
    this.Ctx.WriteString("   ")
    this.Ctx.WriteString(username)
}

URL输入localhost:8080/hello?id=1&username=clearlove。不仅能在网页上看到输出,还能通过控制台看到:

2020/02/03 12:03:17.185 [D] [server.go:2802]  |  ::1| 200 | 0s|   match| GET  /hello   r:/hello

3.2 绑定struct方式

除了上面一个一个的获取请求参数,针对POST请求的表单数据,beego支持直接将表单数据绑定到一个struct变量。

上一节的Demo采用的是URL传入参数,这次采用Post表单的形式传入。首先在viewer文件夹下生成一个HTML文件register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册</title>
</head>
<body>
<form action="/register" method="post">
    <table>
        <tbody>
        <tr>
            <td>用户名</td>
            <td>
                <input type="text" name ="Name" value="">
            </td>
        </tr>
        <tr>
            <td>密码</td>
            <td>
                <input type="password" name ="Pwd" value="">
            </td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="确认">
            </td>
        </tr>
        </tbody>
    </table>
</form>
</body>

然后在控制器中写:

type Users struct {
    Name string
    Pwd  string
}

func (this *RegController) Get(){
    this.TplName="register.html"
}

func (this *RegController) Post(){
    user:=Users{}
    err := this.ParseForm(&user)
    if err!=nil{
        fmt.Println("err:",err)
    }else{
        this.Ctx.WriteString("我是结构体\n")
        this.Ctx.WriteString("Name="+user.Name+"\nPwd="+user.Pwd)
    }
}

这段代码的思路是:首先用户访问localhost:8080/register时,Begoo读取register.html资源。

当用户输入账号密码,点击确认时,触发Post。控制器接收请求,解析到结构体中,然后显示出来。


如同Beego框架学习2中提到的,我们还可以使用标签来控制解析:

比如给属性设置别名:

PlayerName string `form:"Name"`

结果还是没变,因为我们写的就是如此,

this.Ctx.WriteString("Name="+user.PlayerName+...)

同时我们也可以设置不想解析的内容,比如验证码。


type Users struct {
    Name string 
    Pwd  string
        VerCode string  `form:"-"`  //忽略验证码
}

3.3 处理Json请求

有时候会将json请求参数保存在http请求的body里面,所以我们需要解析body的内容。

处理json参数的步骤

  1. 在app.conf配置文件中,添加CopyRequestBody=true
  2. 通过this.Ctx.Input.RequestBody获取请求body的内容
  3. 通过json.Unmarshal反序列化json字符串,将json参数绑定到struct变量。
// 如果json字段跟struct字段名不一样,可以通过json标签设置json字段名
type UserForm struct {
    // 忽略掉Id字段
    Id    int         `json:"-"`
    // json字段名为username
    Name  string      `json:"username"`
    Phone string      
}

控制器代码:

func (this *UserController) Post() {
    // 定义保存json数据的struct对象
    u := UserForm{}

    // 获取body内容
    body := this.Ctx.Input.RequestBody

    // 反序列json数据,结果保存至u
    if err := json.Unmarshal(body, &u); err == nil {
        // 解析参数失败
    }
}

4. 响应请求

第三章主要描述了如何解析用户的请求,而这一章则是如何返回处理过的请求给用户。

4.1 返回json数据

// 如果struct字段名跟json字段名不一样,可以使用json标签,指定json字段名
type User struct {
    // - 表示忽略id字段
    Id       int    `json:"-"`
    Username string `json:"name"`
    Phone    string
}

func (this *UserController) Get() {
    // 定义需要返回给客户端的数据
    user := User{1, "tizi365", "13089818901"}

    // 将需要返回的数据赋值给json字段
    this.Data["json"] = &user

    // 将this.Data["json"]的数据,序列化成json字符串,然后返回给客户端
    this.ServeJSON()
}

4.2 返回XML数据

// 如果struct字段名跟xml字段名不一样,可以使用xml标签,指定xml字段名
type User struct {
    // - 表示忽略id字段
    Id       int    `xml:"-"`
    Username string `xml:"name"`
    Phone    string
}

func (this *UserController) Get() {
    // 定义需要返回给客户端的数据
    user := User{1, "tizi365", "13089818901"}

    // 将需要返回的数据赋值给xml字段
    this.Data["xml"] = &user

    // 将this.Data["xml"]的数据,序列化成xml字符串,然后返回给客户端
    this.ServeXML()
}

4.3 返回网页

func (c *MainController) Get() {
    // 设置模板参数
    c.Data["Website"] = "123.com"
    c.Data["Email"] = "asd@demo.com"

    // 需要渲染的模板, beego会渲染这个模板,然后返回结果
    c.TplName = "index.tpl"
}