其他分享
首页 > 其他分享> > golang beego后端开发框架(二):配置、路由和控制器

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到函数名,定义如下:

比如下面的例子中,我们要为同一个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方法包含以下几类,都要小写就行

如果同时存在 * 和具体的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请求:

 

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()
}

 

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