What It Does

Why It Helps

How It Looks

myStruct := new(MyStruct) errs := binding.Bind(req, myStruct) if errs.Handle(response) { return }

Example

package main import ( "fmt" "net/http" "github.com/mholt/binding" ) // First define a type to hold the data // (If the data comes from JSON, see: http://mholt.github.io/json-to-go) type ContactForm struct { User struct { ID int } Email string Message string } // Then provide a field mapping (pointer receiver is vital) func (cf *ContactForm) FieldMap() binding.FieldMap { return binding.FieldMap{ &cf.User.ID: "user_id", &cf.Email: "email", &cf.Message: binding.Field{ Form: "message", Required: true, }, } } // Now your handlers can stay clean and simple. func handler(resp http.ResponseWriter, req *http.Request) { contactForm := new(ContactForm) errs := binding.Bind(req, contactForm) if errs.Handle(resp) { return } fmt.Fprintf(resp, "From: %d\n", contactForm.User.ID) fmt.Fprintf(resp, "Message: %s\n", contactForm.Message) } func main() { http.HandleFunc("/contact", handler) http.ListenAndServe(":3000", nil) }

Usage

1 Make a struct to hold the data

Form data doesn't require struct tags, but you can use them for deserializing JSON like usual. (Here's a convenient way to convert JSON into a Go struct.)

type LoginForm struct { Username string `json:"user_name"` Password string `json:"password"` }



2 Implement the binding.FieldMapper interface

Field maps are used for form data. If you're only using JSON, you can return an empty field map.

func (l *LoginForm) FieldMap() binding.FieldMap { return binding.FieldMap{ &l.Username: "user_name", &l.Password: "password", } }



3 Use it in your handler

New up your struct, bind the request to it, and optionally handle errors. You have lots of freedom in how you use binding.

func loginHandler(resp http.ResponseWriter, req *http.Request) { loginForm := new(LoginForm) errs := binding.Bind(req, loginForm) if errs.Handle(resp) { return } // ... application logic here ... }

This just scratches the surface. See the README and GoDoc documentation to learn about data validation, error handling, required fields, binding custom types, and everything else.

FAQ

Hey, this is a lot of work.

What is, mapping fields? Bah. It's the same as using struct tags but with a greater degree of flexibility: being able to (optionally!) specify required fields, custom binding functions, and metadata all inline with your type, but out of the way of your application logic, is a huge win.

Why no reflection?

Reflection has its place, and arguably, data binding is a good use case for reflection. Pretty much every comparable library uses it. In fact, this package borrows ideas from the reflection-heavy martini-contrib/binding library. Reflection's downsides are well-known: slow, error-prone, and confusing.

The obscurity of what is really happening and the delicacy of using reflect motivated a new approach. And that's all this package is: a different approach to data binding. Let idiomatic Go shine in all its glory, and see what really can be done without having to use the reflect package.

But type assertions are reflection...

That's debatable. The Go language by itself does not facilitate run-time reflection. Type switches/assertions are inherently way safer and easier to use than the reflect package, which "implements run-time reflection." So anyway, there's no real reflection going on in this package. (Not because reflection is bad, but simply because there is another way.)

...and encoding/json uses reflection!

Ah, yup. Beyond this binding package itself, there are no guarantees what the individual deserializers (other packages) are going to do.

[anything else about reflection]

Reflection is not the point here. Yes, it's "reflectionless data binding for Go" -- but that's just a description. Instead, don't worry about whether reflection is good or bad for this. Just enjoy clear, idiomatic Go code.

What are the goals of this package?

"Keep it lightweight, idiomatic, flexible, and simple, stupid."

What about performance?

What about it? I mean, you can run benchmarks if you want. As with anything else, if you need the absolutest best performance ever invented, don't use a general-purpose library like this. Instead, code up some highly optimized routine yourself for your specific use case. For regular use, we're talking about a difference of microseconds... not a big deal in most cases.

How did this project start?

Initially, it was supposed to be a contributed middleware for Negroni. However, it quickly became clear that there was no reason to make this package middleware: all you need is a function call. So it became a generalized binding package that can be used anywhere, with nearly any framework or none at all, for nearly any request format.

Why isn't this project called "vampire"?

Because there's no black magic here.