Skip to content
a golang web mvc framework, like mvc.
Find file
Latest commit d0c1d3f @QLeelulu Merge pull request #4 from dongwq/master
fixed two error when i use todo example.
Failed to load latest commit information.
examples Update todo.go
form unicode string len
utils conf
views version num
.gitignore add gitignore
Makefile init LICENSE
actionresult.go view engine
controller.go todo example
db.go db count
db_test.go db count
devhelper.go devhelper
doc.go doc
filter.go init
httpcontext.go view engine
log.go log
middleware.go error page
route.go route
route_test.go database, form
server.go view engine
viewengine.go view engine


goku is a Web Mvc Framework for golang, mostly like ASP.NET MVC.

doc & api


To install goku, simply run go get To use it in a program, use import ""

To run example "todo" app, just:

$ cd $GOROOT/src/pkg/
$ go run app.go

maybe you need run todo.sql first.


    package main

    import (

    // routes
    var routes []*goku.Route = []*goku.Route{
        // static file route
            Name:     "static",
            IsStatic: true,
            Pattern:  "/static/(.*)",
        // default controller and action route
            Name:       "default",
            Pattern:    "/{controller}/{action}/{id}",
            Default:    map[string]string{"controller": "home", "action": "index", "id": "0"},
            Constraint: map[string]string{"id": "\\d+"},

    // server config
    var config *goku.ServerConfig = &goku.ServerConfig{
        Addr:           ":8888",
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
        //RootDir:        os.Getwd(),
        StaticPath: "static",
        ViewPath:   "views",
        Debug:      true,

    func init() {
         * project root dir
        _, filename, _, _ := runtime.Caller(1)
        config.RootDir = path.Dir(filename)

         * Controller & Action
            Get("index", func(ctx *goku.HttpContext) goku.ActionResulter {
            return ctx.Html("Hello World")


    func main() {
        rt := &goku.RouteTable{Routes: routes}
        s := goku.CreateServer(rt, nil, config)
        goku.Logger().Logln("Server start on", s.Addr)


    var Routes []*goku.Route = []*goku.Route{
            Name:     "static",
            IsStatic: true,
            Pattern:  "/public/(.*)",
            Name:       "edit",
            Pattern:    "/{controller}/{id}/{action}",
            Default:    map[string]string{"action": "edit"},
            Constraint: map[string]string{"id": "\\d+"},
            Name:    "default",
            Pattern: "/{controller}/{action}",
            Default: map[string]string{"controller": "todo", "action": "index"},
    // create server with the rules
    rt := &goku.RouteTable{Routes: todo.Routes}
    s := goku.CreateServer(rt, nil, nil)


    rt := new(goku.RouteTable)
    rt.Static("staticFile", "/static/(.*)")
        "blog-page", // route name
        "/blog/p/{page}", // pattern
        map[string]string{"controller": "blog", "action": "page", "page": "0"}, // default
        map[string]string{"page": "\\d+"} // constraint
        "default", // route name
        "/{controller}/{action}", // pattern
        map[string]string{"controller": "home", "action": "index"}, // default

Controller And Action

    // add a controller named "home"
        Filters(new(TestControllerFilter)). // this filter is fot controller(all the actions)
        // home controller's index action
        // for http GET
        Get("index", func(ctx *goku.HttpContext) goku.ActionResulter {
        return ctx.Html("Hello World")
    }).Filters(new(TestActionFilter)). // this filter is for home.index action
        // home controller's about action
        // for http POST
        Post("about", func(ctx *goku.HttpContext) goku.ActionResulter {
        return ctx.Raw("About")
  • get /home/index will return Hello World
  • post /home/index will return 404
  • post /home/about will return About


ActionResulter is type interface. all the action must return ActionResulter. you can return an ActionResulter in the action like this:

    // ctx is *goku.HttpContext
    ctx.NotFound("oh no! ):")
    // or you can return a view that
    // will render a template
    ctx.Render("viewName", viewModel)

or you can return a ActionResulter by this

    return &ActionResult{
        StatusCode: http.StatusNotFound,
        Headers:    map[string]string{"Content-Type": "text/html"},
        Body:       "Page Not Found",

for more info, check the code.

View and ViewData

Views are the components that display the application's user interface (UI)

To render a view, you can just return a ViewResut which implement the ActionResulter interface. just like this:

        Get("page", func(ctx *goku.HttpContext) goku.ActionResulter {
        blog := GetBlogByid(10)

        // you can add any val to ViewData
        // then you can use it in template
        // like this: {{ .Data.SiteName }}
        ctx.ViewData["SiteName"] = "My Blog"

        // or you can pass a struct to ViewModel
        // then you can use it in template
        // like this: {{ .Model.Title }}
        // that same as blog.Title
        return ctx.View(blog)

ctx.View() will find the view in these rules:

  1. /{ViewPath}/{Controller}/{action}
  2. /{ViewPath}/shared/{action}

for example, ServerConfig.ViewPath is set to "views", and return ctx.View() in home controller's about action, it will find the view file in this rule:

  1. {ProjectDir}/views/home/about.html
  2. {ProjectDir}/views/shared/about.html

if you want to return a view that specified view name, you can use ctx.Render:

    // it will find the view in these rules:
    //      1. /{ViewPath}/{Controller}/{viewName}
    //      2. /{ViewPath}/shared/{viewName}
    // if viewName start with '/',
    // it will find the view direct by viewpath:
    //      1. /{ViewPath}/{viewName}
    ctx.Render("viewName", ViewModel)

ViewEngine & Template

    // you can add any val to ViewData
    // then you can use it in template
    // like this: {{ .Data.SiteName }}
    ctx.ViewData["SiteName"] = "My Blog"

    blogs := GetBlogs()
    // or you can pass a struct to ViewModel
    // then you can use it in template
    // like this: {{range .Model}} {{ .Title }} {{end}}
    return ctx.View(blogs)

default template engine is golang's template.

    <div class="box todos">
        <h2 class="box">{{ .Data.SiteName }}</h2>
          {{range .Model}}
            <li id="blog-{{.Id}}">



    <!DOCTYPE html>
        {{template "head"}}
      {{template "body" .}}


    {{define "head"}}
        <!-- add css or js here -->

    {{define "body"}}
        I'm main content.

note the dot in {{template "body" .}} , it will pass the ViewData to the sub template.


More Template Engine Support

if you want to use mustache template, check mustache.goku


    type HttpContext struct {
        Request        *http.Request       // http request
        responseWriter http.ResponseWriter // http response
        Method         string              // http method

        //self fields
        RouteData *RouteData             // route data
        ViewData  map[string]interface{} // view data for template
        Data      map[string]interface{} // data for httpcontex
        Result    ActionResulter         // action result
        Err       error                  // process error
        User      string                 // user name
        Canceled  bool                   // cancel continue process the request and return

Form Validation

you can create a form, to valid the user's input, and get the clean value.

    import ""

    func CreateCommentForm() *goku.Form {
        name := NewCharField("name", "Name", true).Range(3, 10).Field()
        nickName := NewCharField("nick_name", "Nick Name", false).Min(3).Max(20).Field()
        age := NewIntegerField("age", "Age", true).Range(18, 50).Field()
        content := NewTextField("content", "Content", true).Min(10).Field()

        form := NewForm(name, nickName, age, content)
        return form

and then you can use this form like this:

    f := CreateCommentForm()

    if f.Valid() {
        // after valid, we can get the clean values
        m := f.CleanValues()
        // and now you can save m to database
    } else {
        // if not valid
        // we can get the valid errors
        errs := f.Errors()

checkout form_test.go


simple database api.

    db, err := OpenMysql("mymysql", "tcp:localhost:3306*test_db/lulu/123456")

    // you can use all the api in golang's database/sql
    _, err = db.Query("select 1")

    // or you can use some simple api provide by goku
    r, err := db.Select("test_blog", SqlQueryInfo{
        Fields: "id, title, content",
        Where:  "id>?",
        Params: []interface{}{0},
        Limit:  10,
        Offset: 0,
        Group:  "",
        Order:  "id desc",

    // insert map
    vals := map[string]interface{}{
        "title": "golang",
        "content": "Go is an open source programming environment that " +
            "makes it easy to build simple, reliable, and efficient software.",
        "create_at": time.Now(),
    r, err := db.Insert("test_blog", vals)

    // insert struct
    blog := TestBlog{
        Title:    "goku",
        Content:  "a mvc framework",
        CreateAt: time.Now(),
    r, err = db.InsertStruct(&blog)

    // get struct
    blog := &TestBlog{}
    err = db.GetStruct(blog, "id=?", 3)

    // get struct list
    qi := SqlQueryInfo{}
    var blogs []Blog
    err := db.GetStructs(&blogs, qi)

    // update by map
    vals := map[string]interface{}{
        "title": "js",
    r, err2 := db.Update("test_blog", vals, "id=?", blog.Id)

    // delete
    r, err := db.Delete("test_blog", "id=?", 8)

checkout db_test.go

DataBase SQL Debug

if you want to debug what the sql query is, set db.Debug to true

    db, err := OpenMysql("mymysql", "tcp:localhost:3306*test_db/username/pwd")
    db.Debug = true

after you set db.Debug to true, while you run a db command, it will print the sql query to the log, juse like this:

2012/07/30 20:58:03 SQL: UPDATE `user` SET friends=friends+? WHERE id=?;
                    PARAMS: [[1 2]]

Action Filter

    type TestActionFilter struct {

    func (tf *TestActionFilter) OnActionExecuting(ctx *goku.HttpContext) (ar goku.ActionResulter, err error) {
        ctx.WriteString("OnActionExecuting - TestActionFilter \n")
    func (tf *TestActionFilter) OnActionExecuted(ctx *goku.HttpContext) (ar goku.ActionResulter, err error) {
        ctx.WriteString("OnActionExecuted - TestActionFilter \n")

    func (tf *TestActionFilter) OnResultExecuting(ctx *goku.HttpContext) (ar goku.ActionResulter, err error) {
        ctx.WriteString("    OnResultExecuting - TestActionFilter \n")

    func (tf *TestActionFilter) OnResultExecuted(ctx *goku.HttpContext) (ar goku.ActionResulter, err error) {
        ctx.WriteString("    OnResultExecuted - TestActionFilter \n")

Order of the filters execution is:

  1. OnActionExecuting
  2. -> Execute Action -> return ActionResulter
  3. OnActionExecuted
  4. OnResultExecuting
  5. -> ActionResulter.ExecuteResult
  6. OnResultExecuted


    type TestMiddleware struct {

    func (tmd *TestMiddleware) OnBeginRequest(ctx *goku.HttpContext) (goku.ActionResulter, error) {
        ctx.WriteString("OnBeginRequest - TestMiddleware \n")
        return nil, nil

    func (tmd *TestMiddleware) OnBeginMvcHandle(ctx *goku.HttpContext) (goku.ActionResulter, error) {
        ctx.WriteString("  OnBeginMvcHandle - TestMiddleware \n")
        return nil, nil
    func (tmd *TestMiddleware) OnEndMvcHandle(ctx *goku.HttpContext) (goku.ActionResulter, error) {
        ctx.WriteString("  OnEndMvcHandle - TestMiddleware \n")
        return nil, nil

    func (tmd *TestMiddleware) OnEndRequest(ctx *goku.HttpContext) (goku.ActionResulter, error) {
        ctx.WriteString("OnEndRequest - TestMiddleware \n")
        return nil, nil

Order of the middleware event execution is:

  1. OnBeginRequest
  2. OnBeginMvcHandle(if not the static file request)
  3. => run controller action (if not the static file request)
  4. OnEndMvcHandle(if not the static file request)
  5. OnEndRequest


To use logger in goku, just:

goku.Logger().Logln("i", "am", "log")
goku.Logger().Errorln("oh", "no!", "Server Down!")

this will log like this:

2012/07/14 20:07:46 i am log
2012/07/14 20:07:46 [ERROR] oh no! Server Down!



View the LICENSE file.

Something went wrong with that request. Please try again.