golang beego后端开发框架(二):配置、路由和控制器
作者:互联网
1. beego参数配置
beego目前支持INI、XML、JSON、YAML格式的配置文件解析,但是默认采用了INI格式解析,用户可以通过简单的配置就可以获得很大的灵活性
1.1 默认配置解析
neego会默认解析当前应用下的 conf/app.conf 文件
当我们使用 bee new 命令新建一个项目时,app.conf 文件默认参数只有以下几个:
appname = pro01 // appname随便改
httpport = 8080 // 服务端口
runmode = dev // 开发模式
这里面可写的参数不是任意的,都会被维护在结构体 beego/server/web#Config 中
beego的参数主要包含了以下这些 https://godoc.org/github.com/beego/beego#pkg-constants
可以在配置文件中配置应用需要的配置信息,比如mysql的连接信息
mysqluser = "root"
mysqlpass = "rootpass"
需要这些配置信息时,只需要使用AppConfig的一系列方法就可以取到数据
user,err := beego.AppConfig.String( "mysqluser" )
AppConfig支持的方法如下:
Set(key, val string) error
String(key string) string
Strings(key string) []string
Int(key string) (int, error)
Int64(key string) (int64, error)
Bool(key string) (bool, error)
Float(key string) (float64, error)
DefaultString(key string, defaultVal string) string
DefaultStrings(key string, defaultVal []string)
DefaultInt(key string, defaultVal int) int
DefaultInt64(key string, defaultVal int64) int64
DefaultBool(key string, defaultVal bool) bool
DefaultFloat(key string, defaultVal float64) float64
DIY(key string) ( interface {}, error)
GetSection(section string) ( map [string]string, error)
SaveConfigFile(filename string) error
1.2 不同级别的配置
在配置文件中里面支持section,可以有不同的 Runmode 的配置,默认优先读取 runmode 下的配置信息,例如下面的配置文件:
appname = beepkg
httpport = 9090
runmode = "dev"
[dev]
httpport = 8080
[prod]
httpport = 8088
[test]
httpport = 8888
上面的配置文件就是在不同 runmode 下解析不同的配置
在dev模式下,服务端口是8080,在prod模式下是8088
读取不同模式下配置参数的方法是 模式::配置参数名 ,比如:
beego.AppConfig.String( "dev::mysqluser" )
1.3 多个配置文件
INI格式支持include方式引用多个配置文件例如下面的例子
app.conf
appname = pro01
httpport = 8080
runmode = dev
include "app2.conf"
app2.conf
runmode = "dev"
autorender = false
recoverpanic = false
viewspath = "myview"
[dev]
httpport = 8080
[prod]
httpport = 8088
[test]
httpport = 8888
1.4 支持环境变量配置
配置文件解析支持从环境变量中获取配置,配置项格式 ${环境变量}
例如下面的配置中优先使用环境变量中配置的 runmode 和 httpport
如果有配置环境变量 ProRunMode 则优先使用该环境变量值;如果不存在或者为空,则使用 “dev” 作为 runmode
runmode = "${ProRunMode||dev}"
httpport = "${ProPort||9090}"
1.5 系统默认参数
参数配置 - beego: 简约 & 强大并存的 Go 应用框架
2. beego路由设置
beego路由存在三种方式:固定路由、正则路由、自动路由
2.1 固定路由
固定路由是最常见的路由方式,一个固定的路由地址加一个控制器模式
固定路由示例如下所示:
beego.Router( "/" , &controllers.MainController{})
beego.Router( "/admin" , &admin.UserController{})
beego.Router( "/admin/index" , &admin.ArticleController{})
beego.Router( "/admin/addpkg" , &admin.AddController{})
2.2 正则路由
形如下面这种路由设置
http://localhost:8080/api/?:id http://localhost:8080/api/:id
这两个路由的默认匹配都是 /api/123类型的,但是第一个路由 /api/可以正常匹配,第二个就会匹配失败
可以在Controller中通过如下方式获取上面的变量,记得添加冒号!
c.Ctx.Input.Param(":id")
2.3 自动路由
自动路由需要先把需要路由的控制器注册到自动路由中
beego.AutoRouter(&controllers.ObjectController{})
那么beego就会通过反射来获取该结构体的所有实现方法,就可以通过下面的方式访问到对应的方法
两个前缀分别为controller的名字和方法名 /:controller/:method
/object/login 调用 ObjectController 中的 Login 方法
/object/logout 调用 ObjectController 中的 Logout 方法
除了两个前缀的匹配外,剩下的 URL beego会自动解析为参数,保存在 this.Ctx.Input.Params中
/object/blog/2013/09/12 调用 ObjectController 中的 Blog 方法,参数如下: map [0:2013 1:09 2:12]
现在已经可以通过自动识别出来下面类似的所有 url,都会把请求分发到 controller
的 simple
方法
后缀名可以通过 this.Ctx.Input.Param(":ext") 来获取
/controller/simple
/controller/simple.html
/controller/simple.json
/controller/simple.xml
2.4 自定义方法和RESTful规则
如果用户期望使用自定义函数名,那么可以使用如下方式:
beego.Router( "/" ,&IndexController{}, "*:Index" )
使用第三个参数,第三个参数就是用来设置对应method到函数名,定义如下:
- * 表示任意的method都执行该函数
- 使用httpmethod:funcname格式来展示
- 多个不同的格式使用 ; 分隔
- 多个method对应同一个funcname,method之间采用 , 分隔
比如下面的例子中,我们要为同一个URL针对不同类型的请求,绑定不同的控制器
beego.Router( "/api/food" ,&RestController{}, "get:ListFood" )
beego.Router( "/api/food" ,&RestController{}, "post:CreateFood" )
beego.Router( "/api/food" ,&RestController{}, "put:UpdateFood" )
beego.Router( "/api/food" ,&RestController{}, "delete:DeleteFood" )
在使用时不推荐采用 ;分隔的方式来加入多个method:func,会使得代码可读性不强
在绑定时支持的http方法包含以下几类,都要小写就行
- * 所有请求
- GET
- POST
- PUT
- DELETE
- PATCH
- OPTIONS
- HEAD
如果同时存在 * 和具体的HTTP方法对应的函数,那么优先执行HTTP方法对应的函数
2.5 namespace
namespace就是需要优先解析的一个URL参数
比如在下面这个例子中,通过NewNamespace方法定义了一个 /v1 namespace
在namesapce中首先通过NSCond()方法来判断是不是满足该 namespace的执行条件
然后后续通过NS+HTTP method格式的方法绑定了很多控制器
最后通过 beego.AddNamespace(ns) 将这个namesapce注册到路由中
//初始化 namespace
ns :=
web.NewNamespace( "/v1" ,
web.NSCond( func (ctx *context.Context) bool {
if ctx.Input.Domain() == "api.beego.vip" {
return true
}
return false
}),
web.NSBefore(auth),
web.NSGet( "/notallowed" , func (ctx *context.Context) {
ctx.Output.Body([]byte( "notAllowed" ))
}),
web.NSRouter( "/version" , &AdminController{}, "get:ShowAPIVersion" ),
web.NSRouter( "/changepassword" , &UserController{}),
web.NSNamespace( "/shop" ,
web.NSBefore(sentry),
web.NSGet( "/:id" , func (ctx *context.Context) {
ctx.Output.Body([]byte( "notAllowed" ))
}),
),
web.NSNamespace( "/cms" ,
web.NSInclude(
&controllers.MainController{},
&controllers.CMSController{},
&controllers.BlockController{},
),
),
)
//注册 namespace
web.AddNamespace(ns)
上面这个代码支持了如下的URL请求:
- GET /v1/notallowed
- GET /v1/version
- GET /v1/changepassword
- POST /v1/changepassword
- GET /v1/shop/123
- GET /v1/cms/ 对应 MainController、CMSController、BlockController 中的注解路由
3. beego控制器
自定义controller的实现需要继承beego.Controller
package controllers
import beego "github.com/beego/beego/v2/server/web"
type UserController struct {
beego.Controller
}
3.1 控制器方法
beego.Controller实现了接口 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()
}
- Init():初始化context、Controller名称、模板名等
- Prepare():用户扩展,这个函数会在下面定义的这些Method方法之前执行,用户可以重写这个函数实现类似于用户验证的工作
- Finish():这个函数是在执行完HTTP method之后执行,默认为nil,用户可以重写这个函数用于关闭数据库、清理缓存等工作
- Render():这个函数主要用来实现渲染模板,如果 beego.AutoRender 为 true 的情况下才会执行
3.2 子类扩展
通过子类对于方法的重写,用户可以实现自己的逻辑,下面是一个实际的例子
type AddController struct {
web.Controller
}
func (this *AddController) Prepare() {
}
// Get 渲染一个模板
func (this *AddController) Get() {
this.Data[ "content" ] = "value"
this.Layout = "admin/layout.html"
this.TplName = "admin/add.tpl"
}
func (this *AddController) Post() {
pkgname := this.GetString( "pkgname" )
content := this.GetString( "content" )
pk := models.GetCruPkg(pkgname)
if pk.Id == 0 {
var pp models.PkgEntity
pp.Pid = 0
pp.Pathname = pkgname
pp.Intro = pkgname
models.InsertPkg(pp)
pk = models.GetCruPkg(pkgname)
}
var at models.Article
at.Pkgid = pk.Id
at.Content = content
models.InsertArticle(at)
this.Ctx.Redirect(302, "/admin/index" )
}
下面是一个比较流行的架构,首先实现一个自己的基类 baseController,实现以下初始化的方法,然后其他所有逻辑继承自该基类
type NestPreparer interface {
NestPrepare()
}
// baseRouter implemented global settings for all other routers.
type baseController struct {
web.Controller
i18n.Locale
user models.User
isLogin bool
}
// Prepare implemented Prepare method for baseRouter.
func (this *baseController) Prepare() {
// page start time
this.Data[ "PageStartTime" ] = time.Now()
// Setting properties.
this.Data[ "AppDescription" ] = utils.AppDescription
this.Data[ "AppKeywords" ] = utils.AppKeywords
this.Data[ "AppName" ] = utils.AppName
this.Data[ "AppVer" ] = utils.AppVer
this.Data[ "AppUrl" ] = utils.AppUrl
this.Data[ "AppLogo" ] = utils.AppLogo
this.Data[ "AvatarURL" ] = utils.AvatarURL
this.Data[ "IsProMode" ] = utils.IsProMode
if app, ok := this.AppController.(NestPreparer); ok {
app.NestPrepare()
}
}
上面定义了基类,大概是初始化了一些变量,最后有一个 Init() 函数中的那个 app 的应用
判断当前运行的Controller是否是NestPreparer的实现,如果是的话调用子类的方法,下面是NestPreparer的实现:
type BaseAdminRouter struct {
baseController
}
func (this *BaseAdminRouter) NestPrepare() {
if this.CheckActiveRedirect() {
return
}
// if user isn't admin, then logout user
if !this.user.IsAdmin {
models.LogoutUser(&this.Controller)
// write flash message
this.FlashWrite( "NotPermit" , "true" )
this.Redirect( "/login" , 302)
return
}
// current in admin page
this.Data[ "IsAdmin" ] = true
if app, ok := this.AppController.(ModelPreparer); ok {
app.ModelPrepare()
return
}
}
func (this *BaseAdminRouter) Get(){
this.TplName = "Get.tpl"
}
func (this *BaseAdminRouter) Post(){
this.TplName = "Post.tpl"
}
这样我们的执行器执行的逻辑是这样的,首先执行 Prepare,这个就是 Go 语言中 struct 中寻找方法的顺序,依次往父类寻找。
执行 BaseAdminRouter
时,查找他是否有 Prepare
方法,没有就寻找 baseController
,找到了,那么就执行逻辑,
然后在 baseController
里面的 this.AppController
即为当前执行的控制器 BaseAdminRouter
,因为会执行 BaseAdminRouter.NestPrepare
方法。
然后开始执行相应的 Get 方法或者 Post 方法
3.3 提前终止运行
我们应用中经常会遇到这种情况,在Prepare阶段进行判断,如果用户认证不通过,则输出一段提示信息然后终止进程
可以使用StopRun来终止执行逻辑,可以在任意地方执行
type RController struct {
beego.Controller
}
func (this *RController) Prepare() {
this.Data[ "json" ] = map [string] interface {}{ "name" : "astaxie" }
this.ServeJSON()
this.StopRun()
}
在调用StopRun()之后,如果还定义了Finish(),那么Finish()不会执行,如果需要释放资源,需要在StopRun()之前手动调用Finish()
标签:web,beego,string,golang,key,Data,路由 来源: https://www.cnblogs.com/aganippe/p/16126586.html