1. 程式人生 > >golang的web框架—beego入門簡介

golang的web框架—beego入門簡介

原文地址:https://my.oschina.net/astaxie/blog/124040

你對beego一無所知?沒關係,這篇文件會很好的詳細介紹beego的各個方面,看這個文件之前首先確認你已經安裝了beego,如果你沒有安裝的話,請看這篇安裝指南

導航

最小應用

一個最小最簡單的應用如下程式碼所示:

package main

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

type MainController struct {
    beego.Controller
}

func(this *MainController)Get() {
    this.Ctx.WriteString("hello world"
) } funcmain() { beego.Router("/", &MainController{}) beego.Run() }

把上面的程式碼儲存為hello.go,然後通過命令列進行編譯並執行:

$ go build main.go
$ ./hello

這個時候你可以開啟你的瀏覽器,通過這個地址瀏覽http://127.0.0.1:8080返回“hello world”

那麼上面的程式碼到底做了些什麼呢?

1、首先我們引入了包github.com/astaxie/beego,我們知道Go語言裡面引入包會深度優先的去執行引入包的初始化(變數和init函式,更多),beego包中會初始化一個BeeAPP的應用,初始化一些引數。

2、定義Controller,這裡我們定義了一個struct為MainController,充分利用了Go語言的組合的概念,匿名包含了beego.Controller,這樣我們的MainController就擁有了beego.Controller的所有方法。

3、定義RESTFul方法,通過匿名組合之後,其實目前的MainController已經擁有了Get、Post、Delete、Put等方法,這些方法是分別用來對應使用者請求的Method函式,如果使用者發起的是POST請求,那麼就執行Post函式。所以這裡我們定義了MainController的Get方法用來重寫繼承的Get函式,這樣當用戶GET請求的時候就會執行該函式。

4、定義main函式,所有的Go應用程式和C語言一樣都是Main函式作為入口,所以我們這裡定義了我們應用的入口。

5、Router註冊路由,路由就是告訴beego,當用戶來請求的時候,該如何去呼叫相應的Controller,這裡我們註冊了請求/的時候,請求到MainController。這裡我們需要知道,Router函式的兩個引數函式,第一個是路徑,第二個是Controller的指標。

6、Run應用,最後一步就是把在1中初始化的BeeApp開啟起來,其實就是內部監聽了8080埠:Go預設情況會監聽你本機所有的IP上面的8080埠

停止服務的話,請按ctrl+c

新建專案

通過如下命令建立beego專案,首先進入gopath目錄

bee create hello

這樣就建立了一個專案hello,目錄結構如下所示

.
├── conf
│   └── app.conf
├── controllers
│   └── default.go
├── main.go
├── models
├── static
│   ├── css
│   ├── img
│   └── js
└── views
    └── index.tpl

開發模式

通過bee建立的專案,beego預設情況下是開發模式。

我們可以通過如下的方式改變我們的模式:

beego.RunMode = "pro"

或者我們在conf/app.conf下面設定如下:

runmode = pro

以上兩種效果一樣。

開發模式中

  • 開發模式下,如果你的目錄不存在views目錄,那麼會出現類似下面的錯誤提示:

    2013/04/13 19:36:17 [W] [stat views: no such file or directory]
  • 模板會自動重新載入不快取。

  • 如果服務端出錯,那麼就會在瀏覽器端顯示如下類似的截圖:

路由設定

路由的主要功能是實現從請求地址到實現方法,beego中封裝了Controller,所以路由是從路徑到ControllerInterface的過程,ControllerInterface的方法有如下:

typeControllerInterface interface {
    Init(ct *Context, cnstring)
    Prepare()
    Get()
    Post()
    Delete()
    Put()
    Head()
    Patch()
    Options()
    Finish()
    Render() error
}

這些方法beego.Controller都已經實現了,所以只要使用者定義struct的時候匿名包含就可以了。當然更靈活的方法就是使用者可以去自定義類似的方法,然後實現自己的邏輯。

使用者可以通過如下的方式進行路由設定:

beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &admin.UserController{})
beego.Router("/admin/index", &admin.ArticleController{})
beego.Router("/admin/addpkg", &admin.AddController{})

為了使用者更加方便的路由設定,beego參考了sinatra的路由實現,支援多種方式的路由:

  • beego.Router("/api/:id([0-9]+)", &controllers.RController{})
    自定義正則匹配 //匹配 /api/123 :id= 123

  • beego.Router("/news/:all", &controllers.RController{})
    全匹配方式 //匹配 /news/path/to/123.html :all= path/to/123.html

  • beego.Router("/user/:username([\w]+)", &controllers.RController{})
    正則字串匹配 //匹配 /user/astaxie :username = astaxie

  • beego.Router("/download/*.*", &controllers.RController{})
    *匹配方式 //匹配 /download/file/api.xml :path= file/api :ext=xml

  • beego.Router("/download/ceshi/*", &controllers.RController{})
    *全匹配方式 //匹配 /download/ceshi/file/api.json :splat=file/api.json

  • beego.Router("/:id:int", &controllers.RController{})
    int型別設定方式 //匹配 :id為int型別,框架幫你實現了正則([0-9]+)

  • beego.Router("/:hi:string", &controllers.RController{})
    string型別設定方式 //匹配 :hi為string型別。框架幫你實現了正則([\w]+)

靜態檔案

Go語言內部其實已經提供了http.ServeFile,通過這個函式可以實現靜態檔案的服務。beego針對這個功能進行了一層封裝,通過下面的方式進行靜態檔案註冊:

beego.SetStaticPath("/static","public")
  • 第一個引數是路徑,url路徑資訊
  • 第二個引數是靜態檔案目錄(相對應用所在的目錄)

beego支援多個目錄的靜態檔案註冊,使用者可以註冊如下的靜態檔案目錄:

beego.SetStaticPath("/images","images")
beego.SetStaticPath("/css","css")
beego.SetStaticPath("/js","js")

設定瞭如上的靜態目錄之後,使用者訪問/images/login/login.png,那麼就會訪問應用對應的目錄下面的images/login/login.png檔案。如果是訪問/static/img/logo.png,那麼就訪問public/img/logo.png檔案。

過濾和中介軟體

beego支援自定義過濾中介軟體,例如安全驗證,強制跳轉等

如下例子所示,驗證使用者名稱是否是admin,應用於全部的請求:

var FilterUser = func(w http.ResponseWriter, r *http.Request) {
    if r.URL.User == nil || r.URL.User.Username() != "admin" {
        http.Error(w, "", http.StatusUnauthorized)
    }
}

beego.Filter(FilterUser)

還可以通過引數進行過濾,如果匹配引數就執行

beego.Router("/:id([0-9]+)", &admin.EditController{})
beego.FilterParam("id", func(rw http.ResponseWriter, r *http.Request) {
    dosomething()
})

當然你還可以通過字首過濾

beego.FilterPrefixPath("/admin", func(rw http.ResponseWriter, r *http.Request) {
    dosomething()
})

控制器設計

基於beego的Controller設計,只需要匿名組合beego.Controller就可以了,如下所示:

type xxxController struct {
    beego.Controller
}

beego.Controller實現了介面beego.ControllerInterface,beego.ControllerInterface定義瞭如下函式:

  • Init(ct *Context, cn string)

    這個函式主要初始化了Context、相應的Controller名稱,模板名,初始化模板引數的容器Data

  • Prepare()

    這個函式主要是為了使用者擴充套件用的,這個函式會在下面定義的這些Method方法之前執行,使用者可以重寫這個函式實現類似使用者驗證之類。

  • Get()

    如果使用者請求的HTTP Method是GET, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Get請求.

  • Post()

    如果使用者請求的HTTP Method是POST, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Post請求.

  • Delete()

    如果使用者請求的HTTP Method是DELETE, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Delete請求.

  • Put()

    如果使用者請求的HTTP Method是PUT, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Put請求.

  • Head()

    如果使用者請求的HTTP Method是HEAD, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Head請求.

  • Patch()

    如果使用者請求的HTTP Method是PATCH, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Patch請求.

  • Options()

    如果使用者請求的HTTP Method是OPTIONS, 那麼就執行該函式,預設是403,使用者繼承的子struct中可以實現了該方法以處理Options請求.

  • Finish()

    這個函式實在執行完相應的http Method方法之後執行的,預設是空,使用者可以在子Strcut中重寫這個函式,執行例如資料庫關閉,清理資料之類的工作

  • Render() error

    這個函式主要用來實現渲染模板,如果beego.AutoRender為true的情況下才會執行。

所以通過子struct的方法重寫,使用者就可以實現自己的邏輯,接下來我們看一個實際的例子:

type AddController struct {
    beego.Controller
}

func (this *AddController) Prepare() {

}

func (this *AddController) Get() {
    this.Data["content"] ="value"
    this.Layout = "admin/layout.html"
    this.TplNames = "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")
}

模板處理

模板目錄

beego中預設的模板目錄是views,使用者可以把你的模板檔案放到該目錄下,beego會自動在該目錄下的所有模板檔案進行解析並快取,開發模式下會每次重新解析,不做快取。當然使用者可以通過如下的方式改變模板的目錄:

beego.ViewsPath = "/myviewpath"

自動渲染

beego中使用者無需手動的呼叫渲染輸出模板,beego會自動的在呼叫玩相應的method方法之後呼叫Render函式,當然如果你的應用是不需要模板輸出的,那麼你可以在配置檔案或者在main.go中設定關閉自動渲染。

配置檔案配置如下:

autorender = false

main.go檔案中設定如下:

beego.AutoRender = false

模板資料

模板中的資料是通過在Controller中this.Data獲取的,所以如果你想在模板中獲取內容{{.Content}},那麼你需要在Controller中如下設定:

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

模板名稱

beego採用了Go語言內建的模板引擎,所有模板的語法和Go的一模一樣,至於如何寫模板檔案,詳細的請參考模板教程

使用者通過在Controller的對應方法中設定相應的模板名稱,beego會自動的在viewpath目錄下查詢該檔案並渲染,例如下面的設定,beego會在admin下面找add.tpl檔案進行渲染:

this.TplNames = "admin/add.tpl"

我們看到上面的模板字尾名是tpl,beego預設情況下支援tpl和html字尾名的模板檔案,如果你的字尾名不是這兩種,請進行如下設定:

beego.AddTemplateExt("你檔案的字尾名")

當你設定了自動渲染,然後在你的Controller中沒有設定任何的TplNames,那麼beego會自動設定你的模板檔案如下:

c.TplNames = c.ChildName + "/" + c.Ctx.Request.Method + "." + c.TplExt

也就是你對應的Controller名字+請求方法名.模板字尾,也就是如果你的Controller名是AddController,請求方法是POST,預設的檔案字尾是tpl,那麼就會預設請求/viewpath/AddController/POST.tpl檔案。

lauout設計

beego支援layout設計,例如你在管理系統中,其實整個的管理介面是固定的,支會變化中間的部分,那麼你可以通過如下的設定:

this.Layout = "admin/layout.html"
this.TplNames = "admin/add.tpl"

在layout.html中你必須設定如下的變數:

{{.LayoutContent}}

beego就會首先解析TplNames指定的檔案,獲取內容賦值給LayoutContent,然後最後渲染layout.html檔案。

目前採用首先把目錄下所有的檔案進行快取,所以使用者還可以通過類似這樣的方式實現layout:

{{template "header.html"}}
處理邏輯
{{template "footer.html"}}

模板函式

beego支援使用者定義模板函式,但是必須在beego.Run()呼叫之前,設定如下:

func hello(instring)(outstring){
    out = in + "world"
    return
}

beego.AddFuncMap("hi",hello)

定義之後你就可以在模板中這樣使用了:

{{.Content | hi}}

目前beego內建的模板函式有如下:

  • markdown

    實現了把markdown文字轉化為html資訊,使用方法{{markdown .Content}}

  • dateformat

    實現了時間的格式化,返回字串,使用方法{{dateformat .Time "2006-01-02T15:04:05Z07:00"}}

  • date

    實現了類似PHP的date函式,可以很方便的根據字串返回時間,使用方法{{date .T "Y-m-d H:i:s"}}

  • compare

    實現了比較兩個物件的比較,如果相同返回true,否者false,使用方法{{compare .A .B}}

  • substr

    實現了字串的擷取,支援中文擷取的完美擷取,使用方法{{substr .Str 0 30}}

  • html2str

    實現了把html轉化為字串,剔除一些script、css之類的元素,返回純文字資訊,使用方法{{html2str .Htmlinfo}}

  • str2html

    實現了把相應的字串當作HTML來輸出,不轉義,使用方法{{str2html .Strhtml}}

  • htmlquote

    實現了基本的html字元轉義,使用方法{{htmlquote .quote}}

  • htmlunquote

    實現了基本的反轉移字元,使用方法{{htmlunquote .unquote}}

request處理

我們經常需要獲取使用者傳遞的資料,包括Get、POST等方式的請求,beego裡面會自動解析這些資料,你可以通過如下方式獲取資料

  • GetString(key string) string
  • GetInt(key string) (int64, error)
  • GetBool(key string) (bool, error)

使用例子如下:

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

如果你需要的資料可能是其他型別的,例如是int型別而不是int64,那麼你需要這樣處理:

func (this *MainController) Post() {
    id := this.Input().Get("id")
    intid, err := strconv.Atoi(id)
}

更多其他的request的資訊,使用者可以通過this.Ctx.Request獲取資訊,關於該物件的屬性和方法參考手冊Request

檔案上傳

在beego中你可以很容易的處理檔案上傳,就是別忘記在你的form表單中增加這個屬性enctype="multipart/form-data",否者你的瀏覽器不會傳輸你的上傳檔案。

檔案上傳之後一般是放在系統的記憶體裡面,如果檔案的size大於設定的快取記憶體大小,那麼就放在臨時檔案中,預設的快取記憶體是64M,你可以通過如下來調整這個快取記憶體大小:

beego.MaxMemory = 1<<22

或者在配置檔案中通過如下設定

maxmemory = 1<<22

beego提供了兩個很方便的方法來處理檔案上傳:

  • GetFile(key string) (multipart.File, *multipart.FileHeader, error)

    該方法主要用於使用者讀取表單中的檔名the_file,然後返回相應的資訊,使用者根據這些變數來處理檔案上傳:過濾、儲存檔案等。

  • SaveToFile(fromfile, tofile string) error

    該方法是在GetFile的基礎上實現了快速儲存的功能

儲存的程式碼例子如下:

func (this *MainController) Post() {
    this.SaveToFile("the_file","/var/www/uploads/uploaded_file.txt"")
}

JSON和XML輸出

beego當初設計的時候就考慮了API功能的設計,而我們在設計API的時候經常是輸出JSON或者XML資料,那麼beego提供了這樣的方式直接輸出:

JSON資料直接輸出,設定content-type為application/json:

func (this *AddController) Get() {
    mystruct := { ... }
    this.Data["json"] = &mystruct
    this.ServeJson()
}

XML資料直接輸出,設定content-type為application/xml:

func (this *AddController) Get() {
    mystruct := { ... }
    this.Data["xml"]=&mystruct
    this.ServeXml()
}

跳轉和錯誤

我們在做Web開發的時候,經常會遇到頁面調整和錯誤處理,beego這這方面也進行了考慮,通過Redirect方法來進行跳轉:

func (this *AddController) Get() {
   this.Redirect("/", 302)
}

@todo 錯誤處理還需要後期改進

response處理

response可能會有集中情況:

  1. 模板輸出

    模板輸出上面模板介紹裡面已經介紹,beego會在執行完相應的Controller裡面的對應的Method之後輸出到模板。

  2. 跳轉

    上一節介紹的跳轉就是我們經常用到的頁面之間的跳轉

  3. 字串輸出

    有些時候我們只是想輸出相應的一個字串,那麼我們可以通過如下的程式碼實現

    this.Ctx.WriteString("ok")

Sessions

beego內建了session模組,目前session模組支援的後端引擎包括memory、file、mysql、redis四中,使用者也可以根據相應的interface實現自己的引擎。

beego中使用session相當方便,只要在main入口函式中設定如下:

beego.SessionOn = true

或者通過配置檔案配置如下:

sessionon = true

通過這種方式就可以開啟session,如何使用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.TplNames = "index.tpl"
}

上面的例子中我們知道session有幾個方便的方法:

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

session操作主要有設定session、獲取session、刪除session

當然你要可以通過下面的方式自己控制相應的邏輯這些邏輯:

sess:=this.StartSession()
defer sess.SessionRelease()

sess物件具有如下方法:

  • sess.Set()
  • sess.Get()
  • sess.Delete()
  • sess.SessionID()

但是我還是建議大家採用SetSession、GetSession、DelSession三個方法來操作,避免自己在操作的過程中資源沒釋放的問題。

關於Session模組使用中的一些引數設定:

  • SessionOn

    設定是否開啟Session,預設是false,配置檔案對應的引數名:sessionon

  • SessionProvider

    設定Session的引擎,預設是memory,目前支援還有file、mysql、redis等,配置檔案對應的引數名:sessionprovider

  • SessionName

    設定cookies的名字,Session預設是儲存在使用者的瀏覽器cookies裡面的,預設名是beegosessionID,配置檔案對應的引數名是:sessionname

  • SessionGCMaxLifetime

    設定Session過期的時間,預設值是3600秒,配置檔案對應的引數:sessiongcmaxlifetime

  • SessionSavePath

    設定對應file、mysql、redis引擎的儲存路徑或者連結地址,預設值是空,配置檔案對應的引數:sessionsavepath

當SessionProvider為file時,SessionSavePath是隻儲存檔案的目錄,如下所示:

beego.SessionProvider = "file"
beego.SessionSavePath = "./tmp"

當SessionProvider為mysql時,SessionSavePath是連結地址,採用go-sql-driver,如下所示:

beego.SessionProvider = "mysql"
beego.SessionSavePath = "username:[email protected](address)/dbname?param=value"

當SessionProvider為redis時,SessionSavePath是redis的連結地址,採用了redigo,如下所示:

beego.SessionProvider = "redis"
beego.SessionSavePath = "127.0.0.1:6379"

Cache設定

beego內建了一個cache模組,實現了類似memcache的功能,快取資料在記憶體中,主要的使用方法如下:

var (
    urllist *beego.BeeCache
)

funcinit() {
    urllist = beego.NewBeeCache()
    urllist.Every = 0 //不過期
    urllist.Start()
}

func(this *ShortController)Post() {
    var result ShortResult
    longurl := this.Input().Get("longurl")
    beego.Info(longurl)
    result.UrlLong = longurl
    urlmd5 := models.GetMD5(longurl)
    beego.Info(urlmd5)
    if urllist.IsExist(urlmd5) {
        result.UrlShort = urllist.Get(urlmd5).(string)
    } else {
        result.UrlShort = models.Generate()
        err := urllist.Put(urlmd5, result.UrlShort, 0)
        if err != nil {
            beego.Info(err)
        }
        err = urllist.Put(result.UrlShort, longurl, 0)
        if err != nil {
            beego.Info(err)
        }
    }
    this.Data["json"] = result
    this.ServeJson()
}

上面這個例子演示瞭如何使用beego的Cache模組,主要是通過beego.NewBeeCache初始化一個物件,然後設定過期時間,開啟過期檢測,在業務邏輯中就可以通過如下的介面進行增刪改的操作:

  • Get(name string) interface{}
  • Put(name string, value interface{}, expired int) error
  • Delete(name string) (ok bool, err error)
  • IsExist(name string) bool

安全的Map

我們知道在Go語言裡面map是非執行緒安全的,詳細的atomic_maps。但是我們在平常的業務中經常需要用到執行緒安全的map,特別是在goroutine的情況下,所以beego內建了一個簡單的執行緒安全的map:

bm := NewBeeMap()
if !bm.Set("astaxie", 1) {
    t.Error("set Error")
}
if !bm.Check("astaxie") {
    t.Error("check err")
}

if v := bm.Get("astaxie"); v.(int) != 1 {
    t.Error("get err")
}

bm.Delete("astaxie")
if bm.Check("astaxie") {
    t.Error("delete err")
}

上面演示瞭如何使用執行緒安全的Map,主要的介面有:

  • Get(k interface{}) interface{}
  • Set(k interface{}, v interface{}) bool
  • Check(k interface{}) bool
  • Delete(k interface{})

日誌處理

beego預設有一個初始化的BeeLogger物件輸出內容到stdout中,你可以通過如下的方式設定自己的輸出:

beego.SetLogger(*log.Logger)

只要你的輸出符合*log.Logger就可以,例如輸出到檔案:

fd,err := os.OpenFile("/var/log/beeapp/beeapp.log", os.O_RDWR|os.O_APPEND, 0644)
if err != nil {
    beego.Critical("openfile beeapp.log:", err)
    return
}
lg := log.New(fd, "", log.Ldate|log.Ltime)
beego.SetLogger(lg)

不同級別的log日誌函式

  • Trace(v ...interface{})
  • Debug(v ...interface{})
  • Info(v ...interface{})
  • Warn(v ...interface{})
  • Error(v ...interface{})
  • Critical(v ...interface{})

你可以通過下面的方式設定不同的日誌分級:

beego.SetLevel(beego.LevelError)

當你程式碼中有很多日誌輸出之後,如果想上線,但是你不想輸出Trace、Debug、Info等資訊,那麼你可以設定如下:

beego.SetLevel(beego.LevelWarning)

這樣的話就不會輸出小於這個level的日誌,日誌的排序如下:

LevelTrace、LevelDebug、LevelInfo、LevelWarning、 LevelError、LevelCritical

使用者可以根據不同的級別輸出不同的錯誤資訊,如下例子所示:

Examples of log messages

  • Trace

    • "Entered parse function validation block"
    • "Validation: entered second 'if'"
    • "Dictionary 'Dict' is empty. Using default value"
  • Debug

    • "Web page requested: http://somesite.com Params='...'"
    • "Response generated. Response size: 10000. Sending."
    • "New file received. Type:PNG Size:20000"
  • Info

    • "Web server restarted"
    • "Hourly statistics: Requested pages: 12345 Errors: 123 ..."
    • "Service paused. Waiting for 'resume' call"
  • Warn

    • "Cache corrupted for file='test.file'. Reading from back-end"
    • "Database 192.168.0.7/DB not responding. Using backup 192.168.0.8/DB"
    • "No response from statistics server. Statistics not sent"
  • Error

    • "Internal error. Cannot process request #12345 Error:...."
    • "Cannot perform login: credentials DB not responding"
  • Critical

    • "Critical panic received: .... Shutting down"
    • "Fatal error: ... App is shutting down to prevent data corruption or loss"

Example

funcinternalCalculationFunc(x, y int)(result int, err error) {
    beego.Debug("calculating z. x:",x," y:",y)
    z := y
    switch {
    case x == 3 :
        beego.Trace("x == 3")
        panic("Failure.")
    case y == 1 :
        beego.Trace("y == 1")
        return 0, errors.New("Error!")
    case y == 2 :
        beego.Trace("y == 2")
        z = x
    default :
        beego.Trace("default")
        z += x
    }
    retVal := z-3
    beego.Debug("Returning ", retVal)

    return retVal, nil
}   

funcprocessInput(input inputData) {
    defer func() {
        if r := recover(); r != nil {
            beego.Error("Unexpected error occurred: ", r)
            outputs <- outputData{result : 0, error : true}
        }
    }()
    beego.Info("Received input signal. x:",input.x," y:", input.y)  

    res, err := internalCalculationFunc(input.x, input.y)   
    if err != nil {
        beego.Warn("Error in calculation:", err.Error())
    }

    beego.Info("Returning result: ",res," error: ",err)     
    outputs <- outputData{result : res, error : err != nil} 
}

funcmain() {
    inputs = make(chan inputData)
    outputs = make(chan outputData)
    criticalChan = make(chan int)
    beego.Info("App started.")

    go consumeResults(outputs)
    beego.Info("Started receiving results.")

    go generateInputs(inputs)
    beego.Info("Started sending signals.")

    for {
        select {
            case input := <- inputs:
                processInput(input)
            case <- criticalChan:
                beego.Critical("Caught value from criticalChan: Go shut down.")
                panic("Shut down due to critical fault.")
        }   
    }
}

配置管理

beego支援解析ini檔案, beego預設會解析當前應用下的conf/app.conf檔案

通過這個檔案你可以初始化很多beego的預設引數

appname = beepkg
httpaddr = "127.0.0.1"
httpport = 9090
runmode ="dev"
autorender = false
autorecover = false
viewspath = "myview"

上面這些引數會替換beego預設的一些引數。

你可以在配置檔案中配置應用需要用的一些配置資訊,例如下面所示的資料庫資訊:

mysqluser = "root"
mysqlpass = "rootpass"
mysqlurls = "127.0.0.1"
mysqldb   = "beego"

那麼你就可以通過如下的方式獲取設定的配置資訊:

beego.AppConfig.String("mysqluser")
beego.AppConfig.String("mysqlpass")
beego.AppConfig.String("mysqlurls")
beego.AppConfig.String("mysqldb")

AppConfig支援如下方法

  • Bool(key string) (bool, error)
  • Int(key string) (int, error)
  • Int64(key string) (int64, error)
  • Float(key string) (float64, error)
  • String(key string) string

系統預設引數

beego中帶有很多可配置的引數,我們來一一認識一下它們,這樣有利於我們在接下來的beego開發中可以充分的發揮他們的作用:

  • BeeApp

    beego預設啟動的一個應用器入口,在應用import beego的時候,在init中已經初始化的。

  • AppConfig

    beego的配置檔案解析之後的物件,也是在init的時候初始化的,裡面儲存有解析conf/app.conf下面所有的引數資料

  • HttpAddr

    應用監聽地址,預設為空,監聽所有的網絡卡IP

  • HttpPort

    應用監聽埠,預設為8080

  • AppName

    應用名稱,預設是beego

  • RunMode

    應用的模式,預設是dev,為開發模式,在開發模式下出錯會提示友好的出錯頁面,如前面錯誤描述中所述。

  • AutoRender

    是否模板自動渲染,預設值為true,對於API型別的應用,應用需要把該選項設定為false,不需要渲染模板。

  • RecoverPanic

    是否異常恢復,預設值為true,即當應用出現異常的情況,通過recover恢復回來,而不會導致應用異常退出。

  • PprofOn

    是否啟用pprof,預設是false,當開啟之後,使用者可以通過如下地址檢視相應的goroutine執行情況

    /debug/pprof
    /debug/pprof/cmdline
    /debug/pprof/profile
    /debug/pprof/symbol

    關於pprof的資訊,請參考官方的描述pprof

  • ViewsPath

    模板路徑,預設值是views

  • SessionOn

    session是否開啟,預設是false

  • SessionProvider

    session的引擎,預設是memory

  • SessionName

    存在客戶端的cookie名稱,預設值是beegosessionID

  • SessionGCMaxLifetime

    session過期時間,預設值是3600秒

  • SessionSavePath

    session儲存路徑,預設是空

  • UseFcgi

    是否啟用fastcgi,預設是false

  • MaxMemory

    檔案上傳預設記憶體快取大小,預設值是1 << 26(64M)

第三方應用整合

beego支援第三方應用的整合,使用者可以自定義http.Handler,使用者可以通過如下方式進行註冊路由:

beego.RouterHandler("/chat/:info(.*)", sockjshandler)

sockjshandler實現了介面http.Handler。

目前在beego的example中有支援sockjs的chat例子,示例程式碼如下:

package main

import (
    "fmt"
    "github.com/astaxie/beego"
    "github.com/fzzy/sockjs-go/sockjs"
    "strings"
)

var users *sockjs.SessionPool = sockjs.NewSessionPool()

funcchatHandler(s sockjs.Session) {
    users.Add(s)
    defer users.Remove(s)

    for {
        m := s.Receive()
        if m == nil {
            break
        }
        fullAddr := s.Info().RemoteAddr
        addr := fullAddr[:strings.LastIndex(fullAddr, ":")]
        m = []byte(fmt.Sprintf("%s: %s", addr, m))
        users.Broadcast(m)
    }
}

type MainController struct {
    beego.Controller
}

func(m *MainController)Get() {
    m.TplNames = "index.html"
}

funcmain() {
    conf := sockjs.NewConfig()
    sockjshandler := sockjs.NewHandler("/chat", chatHandler, conf)
    beego.Router("/", &MainController{})
    beego.RouterHandler("/chat/:info(.*)", sockjshandler)
    beego.Run()
}

通過上面的程式碼很簡單的實現了一個多人的聊天室。上面這個只是一個sockjs的例子,我想通過大家自定義http.Handler,可以有很多種方式來進行擴充套件beego應用。

部署編譯應用

Go語言的應用最後編譯之後是一個二進位制檔案,你只需要copy這個應用到伺服器上,執行起來就行。beego由於帶有幾個靜態檔案、配置檔案、模板檔案三個目錄,所以使用者部署的時候需要同時copy這三個目錄到相應的部署應用之下,下面以我實際的應用部署為例:

$ mkdir /opt/app/beepkg
$ cp beepkg /opt/app/beepkg
$ cp -fr views /opt/app/beepkg
$ cp -fr static /opt/app/beepkg
$ cp -fr conf /opt/app/beepkg

這樣在/opt/app/beepkg目錄下面就會顯示如下的目錄結構:

.
├── conf
│   ├── app.conf
├── static
│   ├── css
│   ├── img
│   └── js
└── views
    └── index.tpl
├── beepkg

這樣我們就已經把我們需要的應用搬到伺服器了,那麼接下來就可以開始部署了,我現在伺服器端用兩種方式來run,

  • Supervisord

  • nohup方式

    nohup ./beepkg &

個人比較推薦第一種方式,可以很好的管理起來應用