Security News
Maven Central Adds Sigstore Signature Validation
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
github.com/mholt/binding
Reflectionless data binding for Go's net/http
net/http
is present (Negroni, gocraft/web, std lib, etc.)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(req *http.Request) 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)
}
package main
import (
"bytes"
"fmt"
"github.com/mholt/binding"
"io"
"log"
"mime/multipart"
"net/http"
)
// We expect a multipart/form-data upload with
// a file field named 'data'
type MultipartForm struct {
Data *multipart.FileHeader `json:"data"`
}
func (f *MultipartForm) FieldMap(req *http.Request) binding.FieldMap {
return binding.FieldMap{
&f.Data: "data",
}
}
// Handlers are still clean and simple
func handler(resp http.ResponseWriter, req *http.Request) {
multipartForm := new(MultipartForm)
errs := binding.Bind(req, multipartForm)
if errs.Handle(resp) {
return
}
// To access the file data you need to Open the file
// handler and read the bytes out.
var fh io.ReadCloser
var err error
if fh, err = multipartForm.Data.Open(); err != nil {
http.Error(resp,
fmt.Sprint("Error opening Mime::Data %+v", err),
http.StatusInternalServerError)
return
}
defer fh.Close()
dataBytes := bytes.Buffer{}
var size int64
if size, err = dataBytes.ReadFrom(fh); err != nil {
http.Error(resp,
fmt.Sprint("Error reading Mime::Data %+v", err),
http.StatusInternalServerError)
return
}
// Now you have the attachment in databytes.
// Maximum size is default is 10MB.
log.Printf("Read %v bytes with filename %s",
size, multipartForm.Data.Filename)
}
func main() {
http.HandleFunc("/upload", handler)
http.ListenAndServe(":3000", nil)
}
You can test from CLI using the excellent httpie client
http -f POST localhost:3000/upload data@myupload
You may optionally have your type implement the binding.Validator
interface to perform your own data validation. The .Validate()
method is called after the struct is populated.
func (cf ContactForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
if cf.Message == "Go needs generics" {
errs = append(errs, binding.Error{
FieldNames: []string{"message"},
Classification: "ComplaintError",
Message: "Go has generics. They're called interfaces.",
})
}
return errs
}
binding.Bind()
and each deserializer returns errors. You don't have to use them, but the binding.Errors
type comes with a kind of built-in "handler" to write the errors to the response as JSON for you. For example, you might do this in your HTTP handler:
if binding.Bind(req, contactForm).Handle(resp) {
return
}
As you can see, if .Handle(resp)
wrote errors to the response, your handler may gracefully exit.
For types you've defined, you can bind form data to it by implementing the Binder
interface. Here's a contrived example:
type MyType map[string]string
func (t *MyType) Bind(fieldName string, strVals []string, errs binding.Errors) binding.Errors {
t["formData"] = strVals[0]
return errs
}
If you can't add a method to the type, you can still specify a Binder
func in the field spec. Here's a contrived example that binds an integer (not necessary, but you get the idea):
func (t *MyType) FieldMap() binding.FieldMap {
return binding.FieldMap{
"number": binding.Field{
Binder: func(fieldName string, formVals []string, errs binding.Errors) binding.Errors {
val, err := strconv.Atoi(formVals[0])
if err != nil {
errs.Add([]string{fieldName}, binding.DeserializationError, err.Error())
}
t.SomeNumber = val
return errs
},
},
}
}
Notice that the binding.Errors
type has a convenience method .Add()
which you can use to append to the slice if you prefer.
The following types are supported in form deserialization by default. (JSON requests are delegated to encoding/json
.)
FAQs
Unknown package
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.