Go web micro-framework - http handlers without the http.Request/http.ResponseWriter clutter
binder is currently used in production.
Documentation : http://godoc.org/github.com/SoCloz/binder
Works with pat or using standard net/http handler.
Without binder :
func ViewItemHandler(w http.ResponseWriter, r *http.Request) {
id := r.URL.Query().Get("id")
// Fetch item from DB
item, err := ...
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
jsonResult, err := json.Marshal(item)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(err.Error()))
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(jsonResult)
}
http.Handle("/items", ViewItemHandler)
With binder :
func ViewItem(i *Item) response.Response {
if i == nil {
return &response.Error{http.StatusNotFound, "not found, sorry"}
}
return &response.Json{i}
}
http.Handle("/items", binder.Wrap(ViewItem, "id"))
binder is compatible with pat (no need to add ":" to your bindings, binder automatically adds it) :
m.Get("/items/:id", binder.Wrap(ViewItem, "id"))
binder binds query string or url parameters to your controller parameters.
func MyAction(param1 string, param2 float32, param3 []string, param4 *int) response.Response {
str := fmt.Sprintf("param1=%v, param2=%v, param3=%v, param4=%v", param1, param2, param3, param4)
return &response.Basic{str}
}
http.Handle("/my_action", binder.Wrap(MyAction, "param1", "param2", "param3", "param4"))
GET /my_action
param1=, param2=0, param3=[], param4=<nil>
GET /my_action?param1=foo¶m2=12.5¶m3=foo,bar,baz¶m4=42
param1=foo, param2=12.5, param3=[foo,bar,baz], param4=0xc210148940
Binder currently binds :
- integers (signed/unsigned)
- floats
- strings
- booleans (true/false, on/off, 1/0)
- slices (comma separated lists : value,value,value)
- pointers (nil if no value found)
- structs
- custom binders
- the request
You can bind an "id" url parameter to a database record using a custom binder.
import(
"github.com/SoCloz/binder"
)
var (
ItemBinder = func(values url.Values, name string, typ reflect.Type) (reflect.Value, bool) {
id := binder.GetValue(values, name)
// loads the object of id "id" from the DB
i, err := [...]
if err != nil {
return reflect.Zero(typ), true
}
return reflect.ValueOf(i), false
}
)
func init() {
binder.RegisterBinder(new(Item), ItemBinder)
}
And then :
func ViewItem(i *Item) response.Response {}
m.Get("/items/:id", binder.Wrap(ViewItem, "id"))
If your controller has a parameter of type http.Request, it will be bound to the current http request. You can wrap it using "" (or omit "*" if it is at the end of the call) :
func ViewItem(id int, r *http.Request) response.Response {}
m.Get("/items/:id", binder.Wrap(ViewItem, "id", "*"))
m.Get("/items/:id", binder.Wrap(ViewItem, "id"))
Struct bindings are defined using field tags :
type Options struct {
OnlyNames string `binder:"only_names"`
Page int `binder:"page"`
Tags []string `binder:"tags"`
}
func ViewItem(id int, opt Options) response.Response {}
And wrapped using "*" :
m.Get("/items/:id", binder.Wrap(ViewItem, "id", "*"))
You can omit all "*" at the end of your Wrap call :
m.Get("/items/:id", binder.Wrap(ViewItem, "id"))
The following responses are possible :
- basic
return &response.Basic{"content"}
- json
return &response.Json{data}
- error
return &response.Error{http.StatusNotFound, "content"}
The status code can be changed using :
r := &response.Json{data}
r.SetStatusCode(http.StatusBadRequest)
binder does not handle your database connections, your model or whatever. Use your favorite tool.
binder does not generate your HTML code. Use your favorite templating engine and return a response.Basic
response.
- binder : bind http headers
- more response types & http headers in responses
See LICENCE
Binding code was largely borrowed from revel - https://github.com/robfig/revel