Last active
May 17, 2019 03:43
-
-
Save xeoncross/9e1869bf17a4e3b386417cd362a7f8fa to your computer and use it in GitHub Desktop.
Simple validation middleware following after the idea of gongular but without doing things at the handler level.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package validation_middleware | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"log" | |
"net/http" | |
"net/http/httptest" | |
"reflect" | |
"testing" | |
"github.com/asaskevich/govalidator" | |
) | |
// What if, instead of setting the handler as the struct we duplicate/populate | |
// we create a middleware that passes the correct object if everything succeds? | |
// This would mean you could still create http.Handler's as you see fit, but | |
// passing this middleware would seem simpler..? | |
// | |
// router.GET('/', ValidateMiddleware(handlers.NewPost, &NewPost{})) | |
// InputNewPost defines the POST params we want | |
type InputNewPost struct { | |
Title string `valid:"alphanum,required"` | |
Email string `valid:"email,required"` | |
Message string `valid:"ascii,required"` | |
Date string `valid:"-"` | |
} | |
// Alternative: https://stackoverflow.com/a/29169727/99923 | |
// func clear(v interface{}) { | |
// p := reflect.ValueOf(v).Elem() | |
// p.Set(reflect.Zero(p.Type())) | |
// } | |
type MyHandler func(w http.ResponseWriter, r *http.Request, i interface{}) | |
// ValidateMiddleware requires object be a struct pointer! | |
// v1 | |
// func ValidateMiddleware(h http.Handler, object interface{}) http.Handler { | |
// v2 | |
func ValidateMiddleware(h MyHandler, object interface{}) http.Handler { | |
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
// Clone struct (avoids race conditions) | |
objectElem := reflect.TypeOf(object).Elem() | |
o := reflect.New(objectElem).Elem() | |
var err error | |
err = json.NewDecoder(r.Body).Decode(o.Addr().Interface()) | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
return | |
} | |
var isValid bool | |
isValid, err = govalidator.ValidateStruct(o.Addr().Interface()) | |
// if err != nil { | |
// http.Error(w, err.Error(), http.StatusInternalServerError) | |
// return | |
// } | |
if !isValid { | |
validation := govalidator.ErrorsByField(err) | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
fmt.Println(validation) | |
return | |
} | |
// v1 | |
// h.ServeHTTP(w, r) | |
// v2 | |
h(w, r, o.Addr().Interface()) | |
}) | |
} | |
func TestMiddleware(t *testing.T) { | |
successResponse := "SUCCESS" | |
data := InputNewPost{ | |
Title: "FooBar", | |
Email: "email@example.com", | |
Message: "Hello there", | |
Date: "yes", | |
} | |
b, err := json.Marshal(data) | |
if err != nil { | |
log.Fatal(err) | |
} | |
req, err := http.NewRequest("POST", "/", bytes.NewReader(b)) | |
if err != nil { | |
t.Fatal(err) | |
} | |
req.Header.Add("Content-Type", "application/json") | |
rr := httptest.NewRecorder() | |
// v1 plain HTTP handler wrapping | |
// h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | |
// w.Write([]byte(successResponse)) | |
// }) | |
// v2, custom function | |
h := func(w http.ResponseWriter, r *http.Request, i interface{}) { | |
in := i.(*InputNewPost) | |
// if in, ok := i.(*InputNewPost); !ok { | |
// http.Error(w, "What???", http.StatusInternalServerError) | |
// return | |
// } | |
e := json.NewEncoder(w) | |
e.SetIndent("", " ") | |
err = e.Encode(in) | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
} | |
// w.Write([]byte(successResponse)) | |
} | |
router := http.NewServeMux() | |
router.Handle("/", ValidateMiddleware(h, &InputNewPost{})) | |
// TODO | |
// router := httprouter.New() | |
// router.POST("/", ValidateMiddleware(h, &InputNewPost{})) | |
router.ServeHTTP(rr, req) | |
got := rr.Body.String() | |
if status := rr.Code; status != http.StatusOK { | |
t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK) | |
t.Error(rr.Body.String()) | |
} | |
if got != successResponse { | |
t.Errorf("handler returned wrong body:\n\tgot: %s\n\twant: %s", got, successResponse) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment