Just a though: Cleaning up JSON http handlers

Written by
Link to Post

https://www.reddit.com/r/golang/comments/bzorbk/just_a_though_cleaning_up_json_http_handlers/ by 

## What am i doing?

I am writing an endpoint which accepts various query parameters eg limit which should <= 500 and default to 100 is not supplied

Why have I done it this way?

I would like to remove all my query param validation logic out of my handler func, the handler func can then focus solely on using repos and writing the repsonse

How am I doing it?

The endpoint struct

// GetAllBooks is the struct that holds the endpoint implementation // it gets all books from db type GetAllBooks struct { // filters limit int //configSettings confs []GetAllBooksConf } // NewBooksIndex constructor func, accepts takes config option, see below func NewBooksIndex(conf ...GetAllBooksConf) GetAllBooks { return GetAllBooks{ confs: conf, } } // NormaliseFilters this will be ran before the handler code on each request // this just loops around the inputted params and mutates the GetAllBooks struct accordingly func (b *GetAllBooks) NormaliseFilters (r *http.Request) { for _, c := range b.confs { c(b, r) } } // Run the actual handler function // i can use limit here safe in the knowledge that it will be what I am expecting func (b *GetAllBooks) Run (w http.ResponseWriter, r *http.Request) { msg := fmt.Sprintf("limit: %d", b.limit) // res, err :=repo.GetAll(limit) // ... w.Write([]byte(msg)) } 

Configuring the endpoint

// GetAllBooksConf this attempts to mutate the GetAllBooks fields to the request query params type GetAllBooksConf func (*GetAllBooks, *http.Request) // ValidateLimit is implementing GetAllBooksConf, here we are checking that // the limit is within range of what we want func ValidateLimit(defaultLimit, maxLimit int) GetAllBooksConf { return func (p *GetAllBooks, r *http.Request) { v := r.URL.Query().Get("limit") if v == "" { p.limit = defaultLimit } else { vInt, err := strconv.Atoi(v) if err != nil { p.limit = defaultLimit } p.limit = vInt } if p.limit > maxLimit { p.limit = maxLimit } } } 

Implementing endpoint

//handlers allBooks := handlers.NewBooksIndex( // default = 100, max = 500 handlers.ValidateLimit(100, 500), ) jsonRouter := JSONRouter { r: mux.NewRouter(), } jsonRouter.WithFilters("/books", &allBooks) if err := http.ListenAndServe(":80", jsonRouter.r); err != nil { r.ResolveLogger().Fatal(err) } ... type JSONRouter struct { r *mux.Router } // WithFilters interface to be implemnted below type WithFilters interface { NormaliseFilters (*http.Request) Run (w http.ResponseWriter, r *http.Request) } // WithFilters on each request normalise the filters, and run the resulting handler func func (r JSONRouter) WithFilters(path string, h WithFilters) { r.r.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) { h.NormaliseFilters(r) h.Run(w, r) }) } 

submitted by /u/johnnMackk
[link] [comments]

Article Tags:
· ·
Article Categories:

Leave a Reply