Documentation ¶
Overview ¶
Generate boilerplate http input and ouput for golang
To ease http development process, enabling reusability and remaining http complient a la go.
In short ¶
Golang HTTP dev at its essense will be
1/ get parameters from http request & validate them ( n times ) 2/ if something is wrong do something about it ( n times ) 3/ do the job given set of parameters (from steps 1/) 4/ if something went wrong do something about it 5/ http respond
Now given 3 (a variing function that does a job), and 1 (n instantiator/validators)
varhandler will generate steps 1, 2, 4 and 5
Variing types of response ¶
The function must return an error. Otherwise return can take multiple forms:
func F(x X, y Y) (err error) // default return status when no error is 200 - OK // if the error is unknown the status will be // InternalServerError - see HandleHTTPErrorWithDefaultStatus // for error types func F(x X, y Y) (status int, err error) // sets returned status if error is nil func F(x X, y Y) (response interface{}, err error) // specific response See HandleHTTPResponse code func F(x X, y Y) (response interface{}, status int, err error) // sets status and does Response Handling if no error is set
Variing parameters ¶
The functions takes one or more arguments. Those arguments need to have http instantiators
HTTPX(r *http.Request) (x X, err error)
Error handling ¶
If an instantiation error occurs:
HandleHTTPErrorWithDefaultStatus(w, r, http.StatusBadRequest, err) // will be called
If the wrapped func returns an error
HandleHTTPErrorWithDefaultStatus(w, r, http.StatusInternalServerError, err) // will be called
Response handling ¶
check HandleHTTPResponse's code
Example ¶
Old way :
myHttpHandler(w http.ResponseWriter, r *http.Request) { var x X err := json.NewDecoder(r.Body).Decode(x) if err != nil { //do something about it return } err := validate(x) if err != nil { //do something about it return } // repeat for y and z // get zz from zz pkg response, status, err := F(x, y, z, zz) if err != nil { // return http.StatusInternalServerError } w.WriteHeader(status) json.NewEncoder(w).Encode(response) }
Now The only interesting part should be the call to F.
New way:
//go:generate varhandler -func F package server import "github.com/azr/generators/varhandler/examples/z" func F(x X, y Y, z *Z, zz z.Z) (resp interface{}, status int, err error) {...} // http instantiators : func HTTPX (r *http.Request) (x X, err error) { err = json.NewDecoder(r.Body).Decode(&x) } func HTTPY (r *http.Request) (Y, error) {...} func HTTPZ (r *http.Request) (*Z, error) {...} func z.HTTPZ(r *http.Request) (z.Z, error) {...}
will be generated:
* calls to HTTPX, HTTPY, HTTPX and z.HTTPZ and their error check
* call to F(x, y, z, zz)
* http response code given F's return arguments
In that case generated code looks like :
func FHandler(w http.ResponseWriter, r *http.Request) { var err error x, err := HTTPX(r) if err != nil { HandleHTTPErrorWithDefaultStatus(w, r, http.StatusBadRequest, err) return } y, err := HTTPY(r) if err != nil { HandleHTTPErrorWithDefaultStatus(w, r, http.StatusBadRequest, err) return } z, err := HTTPZ(r) if err != nil { HandleHTTPErrorWithDefaultStatus(w, r, http.StatusBadRequest, err) return } zz, err := z.HTTPZ(r) if err != nil { HandleHTTPErrorWithDefaultStatus(w, r, http.StatusBadRequest, err) return } resp, status, err := F(x, y, z, zz) if err != nil { HandleHTTPErrorWithDefaultStatus(w, r, http.StatusInternalServerError, err) return } if status != 0 { // code generated if status is returned by F w.WriteHeader(status) } if resp != nil { // code generated if resp object is returned by F HandleHTTPResponse(w, r, resp) } } //Helper funcs func HandleHTTPErrorWithDefaultStatus(w http.ResponseWriter, r *http.Request, status int, err error) { type HttpError interface { HttpError() (error string, code int) } type SelfHttpError interface { HttpError(w http.ResponseWriter) } switch t := err.(type) { default: w.WriteHeader(status) case HttpError: err, code := t.HttpError() http.Error(w, err, code) case http.Handler: t.ServeHTTP(w, r) case SelfHttpError: t.HttpError(w) } } func HandleHTTPResponse(w http.ResponseWriter, r *http.Request, resp interface{}) { type Byter interface { Bytes() []byte } type Stringer interface { String() string } switch t := resp.(type) { default: // I don't know that type ! case http.Handler: t.ServeHTTP(w, r) // resp knows how to handle itself case Byter: w.Write(t.Bytes()) case Stringer: w.Write([]byte(t.String())) case []byte: w.Write(t) } }
Code copyied from github.com/azr/generators/varhandler/varhandler_helpers.go; DO NOT EDIT