beego框架mvc流程

beego框架是典型的MVC模式,下面就来具体看一下它的流程。

一、新建项目

在进行了beego 的安装 和 bee 工具的安装后可以使用bee命令创建项目,进入 $GOPATH/src 所在的目录,执行bee new myproject 就可创建名为 myproject 的项目。这是一个典型的MVC架构的应用,main.go 是入口文件,默认端口8080。

二、路由设置

下面是main.go,go的执行过程如下:

package main

import (
	_ "myproject/routers"
	"github.com/astaxie/beego"
)

func main() {
	beego.Run()
}

在这里插入图片描述
根据流程分析在main.go中先执行了_ "myproject/routers"的内容,router.go代码如下,

package routers

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

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

在init()中执行了路由注册beego.Router, 这个函数的功能是映射URL到controller,第一个参数是URL(用户请求的地址),这里我们注册的是 /,也就是我们访问的不带任何参数的URL,第二个参数是对应的 Controller,也就是我们即将把请求分发到那个控制器来执行相应的逻辑。
最后会执行beego.Run()方法,在执行内部做了解析配置文件、执行用户的hookfunc、是否开启 session、是否编译模板、是否开启文档功能、是否启动管理模块和监听服务端口等。

·解析配置文件
beego 会自动在 conf 目录下面去解析相应的配置文件 app.conf,这样就可以通过配置文件配置一些例如开启的端口,是否开启 session,应用名称等各种信息。

appname = myproject
httpport = 8080
runmode = dev

·执行用户的hookfunc
beego会执行用户注册的hookfunc,默认已经注册了mime,用户可以通过函数AddAPPStartHook注册自己的启动函数。

func initBeforeHTTPRun() {
	//init hooks
	AddAPPStartHook(
		registerMime,
		registerDefaultErrorHandler,
		registerSession,
		registerTemplate,
		registerAdmin,
		registerGzip,
	)

	for _, hk := range hooks {
		if err := hk(); err != nil {
			panic(err)
		}
	}
}

·是否开启 session
会根据上面配置文件的分析之后判断是否开启 session,如果开启的话就初始化全局的 session。
·是否编译模板
beego 会在启动的时候根据配置把 views 目录下的所有模板进行预编译,然后存在 map 里面,这样可以有效的提高模板运行的效率,无需进行多次编译。
·是否开启文档功能
根据EnableDocs配置判断是否开启内置的文档路由功能
·是否启动管理模块
beego 目前做了一个很帅的模块,应用内监控模块,会在 8088 端口做一个内部监听,我们可以通过这个端口查询到 QPS、CPU、内存、GC、goroutine、thread 等各种信息。
·监听服务端口
这是最后一步也就是我们看到的访问 8080 看到的网页端口,内部其实调用了 ListenAndServe,充分利用了 goroutine 的优势。
一旦 run 起来之后,我们的服务就监听在两个端口了,一个服务端口 8080 作为对外服务,另一个 8088 端口实行对内监控。

beego.Run()
// Run beego application.
// beego.Run() default run on HttpPort
// beego.Run("localhost")
// beego.Run(":8089")
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {

	initBeforeHTTPRun()

	if len(params) > 0 && params[0] != "" {
		strs := strings.Split(params[0], ":")
		if len(strs) > 0 && strs[0] != "" {
			BConfig.Listen.HTTPAddr = strs[0]
		}
		if len(strs) > 1 && strs[1] != "" {
			BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
		}

		BConfig.Listen.Domains = params
	}

	BeeApp.Run()
}

在 beego.Run()里会先进行initBeforeHTTPRun(),即beego应用启动前的初始化:

func initBeforeHTTPRun() {
	//init hooks
	AddAPPStartHook(
		registerMime,
		registerDefaultErrorHandler,
		registerSession,
		registerTemplate,
		registerAdmin,
		registerGzip,
	)

	for _, hk := range hooks {
		if err := hk(); err != nil {
			panic(err)
		}
	}
}

在初始化时主要是注册一些hook,默认注册registerMime,即MIME(Multipurpose Internet Mail Extensions) 描述消息内容类型的因特网标准,包含文本、图像、音频、视频以及其他应用程序专用的数据。自己可以结合业务添加注册,如DefaultErrorHandler,Session(会开启GC),Template,Gzip。registerAdmin是当beego允许admin时,执行beeAdminApp进行系统监控、性能检测、访问统计和健康检查等,即开启8088监控端口。

回到beego.Run()中,有一个if语句,主要就是当beego.Run()中有参数时更改地址端口的。再往下BeeApp.Run()是真正开启beego应用的,主要运行了cgi服务(公共网关接口),graceful 模式(热重启,程序不中断的重启),normal模式。

三、controller

在controllers目录下定义 MainController的控制器,MainController 自动拥有了 beego.Controller 的所有方法。beego.Controller拥有Init、Prepare、Post、Get、Delete、Head等很多方法,可以通过重写的方式来实现这些方法。在 routers里会根据路由选择不同的控制器,在控制器里会根据Method选择不同执行方法。我们可以通过各种方式获取数据,然后赋值到 this.Data 中,这是一个用来存储输出数据的 map,可以赋值任意类型的值。this.TplName 是需要渲染的模板,这里指定了 index.tpl,如果用户不设置该参数,那么默认会去到模板目录的 Controller/<方法名>.tpl 查找,例如上面的方法会去 MainController/Get.tpl。如果 MainController/Get.tpl不存在会报错。

package controllers

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

type MainController struct {
	beego.Controller
}

func (c *MainController) Get() {
	c.Data["Website"] = "beego.me"
	c.Data["Email"] = "astaxie@gmail.com"
	c.TplName = "index.tpl"
}

在这里插入图片描述

1、参数配置

beego 默认采用了 INI 格式解析。beego 默认会解析当前应用下的 conf/app.conf 文件。可以通过AppConfig 的方法获取配置信息。

beego.AppConfig.String("mysqluser")
beego.AppConfig.String("mysqlpass")
beego.AppConfigPath = "conf/app2.conf"        //设置配置文件
2、路由设置

beego 存在三种方式的路由:固定路由、正则路由、自动路由。应用中的大多数路由都会定义在 routers/router.go 文件中。
2.1基础路由
所有的支持的基础函数如下所示:
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)

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

2.2 RESTful Controller 路由
固定路由也就是全匹配的路由,个固定的路由,一个控制器,然后根据用户请求方法不同请求控制器中对应的方法,典型的 RESTful 方式。

beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &admin.UserController{})

为了用户更加方便的路由设置,路由设置采用正则方式。

beego.Router("/api/?:id", &controllers.RController{})
默认匹配 //匹配 /api/123 :id = 123 可以匹配/api/这个URL

如果期望自定义函数名,那么可以使用如下方式:

beego.Router("/",&IndexController{},"*:Index")

*表示任意的 method 都执行该函数
使用 httpmethod:funcname 格式来展示
多个不同的格式使用 ; 分割
多个 method 对应同一个 funcname,method 之间通过 , 来分割

2.3注解路由
beego1.3版本开始支持了注解路由,用户无需在router中注册路由,只需要Include相应地controller,然后在controller的method方法上面写上router注释(// @router)就可以了.

beego.Include(&CMSController{})
type CMSController struct {
	beego.Controller
}

func (c *CMSController) URLMapping() {
	c.Mapping("StaticBlock", c.StaticBlock)
	c.Mapping("AllBlock", c.AllBlock)
}


// @router /staticblock/:key [get]
func (this *CMSController) StaticBlock() {

}

// @router /all/:key [get]
func (this *CMSController) AllBlock() {

}
3、控制器

Controller 的设计,只需要匿名组合 beego.Controller 就可以了。

type xxxController struct {
    beego.Controller
}

xxxController会具有beego.Controller的所有方法,beego.Controller实现了接口 beego.ControllerInterface,beego.ControllerInterface方法如下:

type ControllerInterface interface {
   Init(ct *context.Context, controllerName, actionName string, app interface{})
   Prepare()
   Get()
   Post()
   Delete()
   Put()
   Head()
   Patch()
   Options()
   Trace()
   Finish()
   Render() error
   XSRFToken() string
   CheckXSRFCookie() bool
   HandlerFunc(fn string) bool
   URLMapping()
}
4、XSRF过滤

当前防范 XSRF 的一种通用的方法,是对每一个用户都记录一个无法预知的 cookie 数据,然后要求所有提交的请求(POST/PUT/DELETE)中都必须带有这个 cookie 数据。如果此数据不匹配 ,那么这个请求就可能是被伪造的。
在应用配置文件中加上 enablexsrf 设定:

enablexsrf = true
xsrfkey = 61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o
xsrfexpire = 3600  

在 main 入口处设置:

beego.EnableXSRF = true
beego.XSRFKEY = "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o"
beego.XSRFExpire = 3600  //过期时间,默认60秒

如果你开启了这个机制,那么在所有被提交的表单中,你都需要加上一个域来提供这个值。你可以通过在模板中使用 专门的函数 XSRFFormHTML() 来做到这一点:

func (this *HomeController) Get(){        
   this.Data["xsrfdata"]=template.HTML(this.XSRFFormHTML())
}

然后在模板中这样设置:

<form action="/new_message" method="post">
 {{ .xsrfdata }}
 <input type="text" name="message"/>
 <input type="submit" value="Post"/>
</form>
5.请求数据处理

5.1 直接解析
一个一个获取用户传递的数据

func (this *MainController) Post() {
   jsoninfo := this.GetString("jsoninfo")
   if jsoninfo == "" {
   	this.Ctx.WriteString("jsoninfo is empty")
   	return
   }
}

解析到 struct

定义struct:
type user struct {
   Id    int         `form:"-"`
   Name  interface{} `form:"username"`
   Age   int         `form:"age"`
   Email string
}
表单:
<form id="user">
   名字:<input name="username" type="text" />
   年龄:<input name="age" type="text" />
   邮箱:<input name="Email" type="text" />
   <input type="submit" value="提交" />
</form>
Controller 里解析:
func (this *MainController) Post() {
   u := user{}
   if err := this.ParseForm(&u); err != nil {
   	//handle error
   }
}

5.2 获取 Request Body

  1. 在配置文件里设置 copyrequestbody = true
  2. 在 Controller 中
func (this *ObjectController) Post() {
   var ob models.Object
   json.Unmarshal(this.Ctx.Input.RequestBody, &ob)
   objectid := models.AddOne(ob)
   this.Data["json"] = "{\"ObjectId\":\"" + objectid + "\"}"
   this.ServeJson()
}
6、session 控制

开启 session
在配置中开启:

sessionon = true

或main.go中

beego.SessionOn = true

使用 session

func (this *MainController) Get() {
   v := this.GetSession("asta")
   if v == nil {
   	this.SetSession("asta", int(1))
   	this.Data["num"] = 0
   } else {
   	this.SetSession("asta", v.(int)+1)
   	this.Data["num"] = v.(int)
   }
   this.TplName = "index.tpl"
}

session 有几个方便的方法:

SetSession(name string, value interface{})
GetSession(name string) interface{}
DelSession(name string)
SessionRegenerateID()
DestroySession()

7、过滤器

beego 支持自定义过滤中间件,过滤器函数如下所示:

beego.InsertFilter(pattern string, postion int, filter FilterFunc, skip ...bool)

pattern 路由规则,可以根据一定的规则进行路由,如果你全匹配可以用 *
postion 执行 Filter 的地方,四个固定参数如下,分别表示不同的执行过程

  • BeforeRouter 寻找路由之前
  • BeforeExec 找到路由之后,开始执行相应的 Controller 之前
  • AfterExec 执行完 Controller 逻辑之后执行的过滤器
  • FinishRouter 执行完逻辑之后执行的过滤器

filter filter 函数 type FilterFunc func(*context.Context)
skip bool 表示如果有输出的情况下是否执行这个Filter,默认是false,只要有输出就全部跳过。

8、错误处理

在beego.Run之前采用beego.ErrorController注册错误处理函数

package main

import (
	_ "btest/routers"
	"btest/controllers"

	"github.com/astaxie/beego"
)

func main() {
	beego.ErrorController(&controllers.ErrorController{})
	beego.Run()
}
package controllers

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

type ErrorController struct {
	beego.Controller
}

func (c *ErrorController) Error404() {
	c.Data["content"] = "page not found"
	c.TplName = "404.tpl"
}

Error404函数其实调用对应的就是Abort(“404”)

9、日志处理

beego 的日志处理是基于 logs 模块搭建的,内置了一个变量 BeeLogger,默认是 logs.BeeLogger 类型,初始化了 console,也就是默认输出到 console。
一般在程序中我们使用如下的方式进行输出:

beego.Emergency("this is emergency")
beego.Alert("this is alert")
beego.Critical("this is critical")
beego.Error("this is error")
beego.Warning("this is warning")
beego.Notice("this is notice")
beego.Informational("this is informational")
beego.Debug("this is debug")

如果要把日志输出到文件,可设置如下

beego.SetLogger("file", `{"filename":"logs/test.log"}`)

这个默认情况就会同时输出到两个地方,一个 console,一个 file。

四、model

go的model采用beego ORM 框架,可通过go get github.com/astaxie/beego/orm 安装使用。

1、数据库的设置

ORM 支持mysql、Postgres、Sqlite三种数据库,ORM 默认使用 time.Local 本地时区。
RegisterDriver如下:

import (
   _ "github.com/go-sql-driver/mysql"
)
func init() {
   //参数1   driverName    参数2   数据库类型
   orm.RegisterDriver("mysql", orm.DRMySQL)
   }

RegisterDataBase如下:
ORM 必须注册一个别名为 default 的数据库,作为默认使用。

// 参数1        数据库的别名,用来在ORM中切换数据库使用
// 参数2        driverName
// 参数3        对应的链接字符串
// 参数4(可选)  设置最大空闲连接
// 参数5(可选)  设置最大数据库连接 (go >= 1.2)
maxIdle := 30
maxConn := 30
orm.RegisterDataBase("default", "mysql", "root:root@/orm_test?charset=utf8", maxIdle, maxConn)

最大空闲连接,最大数据库连接也可后面通过别名设置

orm.SetMaxIdleConns("default", 30)
orm.SetMaxOpenConns("default", 30)
2、注册模型

如果只使用 Raw 查询和 map struct,是无需这一步的。如果使用 orm.QuerySeter 进行高级查询的话,这个是必须的。
RegisterModel在models.go 文件的 init 函数中进行注册:

package models

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

type User struct {
	Id          int
	Name        string
	Profile     *Profile   `orm:"rel(one)"` // OneToOne relation
	Post    	[]*Post `orm:"reverse(many)"` 

type Profile struct {
	Id          int
	Age         int16
	User        *User   `orm:"reverse(one)"`
}

type Post struct {
    Id    int
    Title string
    User  *User  `orm:"rel(fk)"`	
    //Tags  []*Tag `orm:"rel(m2m)"`
}

func init() {
	orm.RegisterModel(new(User), new(Profile), new(Post))
}
3、ORM 接口使用

在types.go下有Ormer interface,使用时创建一个 Ormer,在进行切换数据库、事务处理以及其任何查询都会作用于 Ormer 对象上。

var o Ormer
o = orm.NewOrm() // 创建一个 Ormer
// NewOrm 的同时会执行 orm.BootStrap (整个 app 只执行一次),用以验证模型之间的定义并缓存。

//注册数据库
orm.RegisterDataBase("db1", "mysql", "root:root@/orm_db2?charset=utf8")
o.Using("db1")     //默认使用 default 数据库,无需调用 Using

//原生SQL。Raw 函数,返回一个 RawSeter 用以对设置的 sql 语句和参数进行操作
var r RawSeter
r = o.Raw("UPDATE user SET name = ? WHERE name = ?", "slene", "testing").Exec()  //update user testing's name to slene

//Ormer对象的CRUD
user := new(User)
user.Name = "slene"
o.Insert(user)
user.Name = "Your"
o.Update(user)
o.Read(user)
o.Delete(user)

//高级查询。传入表名,或者 Model 对象,返回一个 QuerySeter对象。如果表没有定义过,会立刻 panic
var qs QuerySeter
qs = o.QueryTable("user")

五、View

beego 的模板处理引擎采用的是 Go 内置的 html/template 包进行处理,而且 beego 的模板处理逻辑是采用了缓存编译方式,也就是所有的模板会在 beego 应用启动的时候全部编译然后缓存在 map 里面。

使用 {{ 和 }} 作为左右标签,没有其他的标签符号。
使用 . 来访问当前位置的上下文
使用 $ 来引用当前模板根级的上下文
使用 $var 来访问创建的变量

beego 中默认的模板目录是 views。Go 语言的默认模板采用了 {{ 和 }} 作为左右标签。模板中的数据是通过在 Controller 中 this.Data 获取的,所以如果你想在模板中获取内容 {{.Content}} ,那么你需要在 Controller 中如下设置:

this.Data["Content"] = "value"

用户通过在 Controller 的对应方法中设置相应的模板名称,beego 会自动的在 viewpath 目录下查询该文件并渲染。

this.TplName = "index.tpl"

beego默认支持 tpl 和 html 的后缀名,采用HTML+CSS+JS结构。在 Controller 里面把数据赋值给了 data(map 类型),然后在模板中通过 key 访问。

<!DOCTYPE html>

<html>
  	<head>
    	<title>Beego</title>
    	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	</head>
  	
  	<body>
  		<header class="hero-unit" style="background-color:#A9F16C">
			<div class="container">
			<div class="row">
			  <div class="hero-text">
			    <h1>Welcome to Beego!</h1>
			    <p class="description">
			    	Beego is a simple & powerful Go web framework which is inspired by tornado and sinatra.
			    <br />
			    	Official website: <a href="http://{{.Website}}">{{.Website}}</a>
			    <br />
			    	Contact me: {{.Email}}
			    </p>
			  </div>
			</div>
			</div>
		</header>
	</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值