Skip to content

Instantly share code, notes, and snippets.

@husobee
Created January 8, 2016 20:07
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save husobee/8c5284786a741e2a1909 to your computer and use it in GitHub Desktop.
Save husobee/8c5284786a741e2a1909 to your computer and use it in GitHub Desktop.
input validation sanely
package main
import (
"encoding/json"
"errors"
"net/http"
"github.com/asaskevich/govalidator"
)
// main - a simple main entry point
func main() {
srv := &http.Server{
Addr: ":1234",
Handler: f,
}
srv.ListenAndServe()
}
// f - our only application handler to demonstrate validation
var f http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
// we have an input structure, of type Thing
var thing *Thing
// we want to decode and validate Thing from request body
err := decodeAndValidate(r, thing)
// there was an error with Thing
if err != nil {
// send a bad request back to the caller
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("bad request"))
return
}
// it was decoded, and validated properly, success
w.WriteHeader(http.StatusOK)
w.Write([]byte("ok"))
}
// decodeAndValidate - entrypoint for deserialization and validation
// of the submission input
func decodeAndValidate(r *http.Request, v InputValidation) error {
// json decode the payload - obviously this could be abstracted
// to handle many content types
if err := json.NewDecoder(r.Body).Decode(v); err != nil {
return err
}
defer r.Body.Close()
// peform validation on the InputValidation implementation
return v.Validate(r)
}
// InputValidation - an interface for all "input submission" structs used for
// deserialization. We pass in the request so that we can potentially get the
// context by the request from our context manager (future net.http will include
// a context in the request)
type InputValidation interface {
Validate(r *http.Request) error
}
var (
// ErrInvalidUUID - error when we have a UUID validation issue
ErrInvalidUUID = errors.New("invalid uuid")
// ErrInvalidName - error when we have an invalid name
ErrInvalidName = errors.New("invalid name")
)
// Thing - is our implemenation of InputValidation and the structure we will
// deserialize into
type Thing struct {
ID string `json:"id"`
Name string `json:"name"`
Category string `json:"category"`
}
// Validate - implementation of the InputValidation interface
func (t Thing) Validate(r *http.Request) error {
// validate the ID is a uuid
if !govalidator.IsUUID(t.ID) {
return ErrInvalidUUID
}
// validate the name is not empty or missing
if govalidator.IsNull(t.Name) {
return ErrInvalidName
}
return nil
}
@sengkathir
Copy link

json.NewDecoder(r.Body).Decode(v) returns error for valid input.

@jian-en
Copy link

jian-en commented Nov 28, 2017

line 23: should be -> var thing Thing

@jian-en
Copy link

jian-en commented Nov 28, 2017

line 25: should be -> err := decodeAndValidate(r, &thing)

@elsonwu
Copy link

elsonwu commented Feb 28, 2018

How to validate the value is posted from client or the default value from go itself?

For example if you have a bool field, IsOnSold

type Thing struct {
ID string json:"id"
Name string json:"name"
Category string json:"category"
IsOnSold bool json:"is_on_sold"
}

If the client only POST id, name, and category, but without this is_on_sold, and it has a default false value, so you thought it POST it, then it might cause issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment