Documentation
¶
Overview ¶
Package hutil contains helper functions and types to write HTTP handlers with Go.
It is opiononated and minimalist: * `ShiftPath` is used to build routing * `Chain` is used to build a chain of middlewares * `NewLoggingMiddleware` is used to create a middleware which logs the requests. It is compatible with `Chain`
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ShiftPath ¶
ShiftPath splits the path into the first component and the rest of the path. The returned head will never have a slash in it, if the path has no tail head will be empty. The tail will never have a trailing slash.
Example ¶
var urlPath = "/search/myhome/inventory" var head string head, urlPath = ShiftPath(urlPath) switch head { case "search": // handler.search(w, req) case "feed": // handler.feed(w, req) default: // handler.home(w, req) } fmt.Printf("head: %s\n", head) fmt.Printf("url path: %s\n", urlPath)
Output: head: search url path: /myhome/inventory
Types ¶
type Middleware ¶
Middleware is a function taking a HTTP handler and returning a new handler that wraps it. For example, you could have a middleware that injects a session in the request's context:
// Create a middleware that injects a session in the request context func sessionMiddleware(next http.Handler) Middleware { return func(w http.ResponseWriter, req *http.Request) { session := fetchSession(req) ctx := contet.WithValue(req.Context(), "session", session) req = req.WithContext(ctx) next.ServeHTTP(w, req) } }
Note that the you _have_ to call `next.ServeHTTP` otherwise requests stop in this handler.
Next you wrap your own handler with this middleware:
myHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ... ) handler := sessionMiddleware(myHandler)
Now whenever your handler is called it can easily get the session
func NewLoggingMiddleware ¶
func NewLoggingMiddleware(logger *zap.Logger) Middleware
NewLoggingMiddleware returns a middleware that logs the requests and the results of the request as processed by the next middleware (or handler) in the chain.
The middleware doesn't log by itself, instead the logFn you pass should do that. THe middleware merely gives you the data to log.
Note about the execution time. Depending on where in the chain you place the logging handler, you will get different execution times. Take the following chain example: Middleware1 -> Logging -> FinalHandler (your business-logic handler, or muxer for example)
The logging middleware, when called, will start counting AFTER the two handlers before in the chain, meaning it will only measure the execution time of the final handler. This isn't always what you want, because if you have a middleware in the chain that can take some measurable time, you probably want to count it too in the execution time. Thus, make sure you place the logging handler at the correct place in the chain.
type MiddlewareStack ¶
type MiddlewareStack []Middleware
MiddlewareStack as its name imply, stacks middlewares. You stack a middleware by calling `Use`. Keep in mind that the first middleware you stack will be the middleware that wraps everything else, this means if you have something like a logging middleware it should be stacked first.
Here is an example:
var stack MiddlewareStack stack.Use(newLoggingMiddleware()) stack.Use(newRateLimitMiddleware()) stack.Use(newSessionMiddleware()) stack.Use(newUserMiddleware())
This will create the following chain of middleware calls: logging -> ratelimit -> session -> user
Example ¶
createMiddleware := func(buf *bytes.Buffer, s string) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { buf.WriteString(s) next.ServeHTTP(w, req) }) } } var ( s MiddlewareStack buf bytes.Buffer ) var ( m1 = createMiddleware(&buf, "m1") m2 = createMiddleware(&buf, "m2") m3 = createMiddleware(&buf, "m3") ) s.Use(m1) s.Use(m2) s.Use(m3) ts := httptest.NewServer(s.Handler(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { fmt.Fprint(w, "foobar") }))) defer ts.Close() res, _ := http.Get(ts.URL) data, _ := io.ReadAll(res.Body) defer res.Body.Close() fmt.Println(string(data)) fmt.Println(buf.String())
Output: foobar m1m2m3
func (MiddlewareStack) Handler ¶
func (s MiddlewareStack) Handler(h http.Handler) http.Handler
Handler wraps the provided final handler with all the middleware stacked and returns a http.Handler instance.
Here is an example:
myHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ... ) var stack MiddlewareStack stack.Use(newLoggingMiddleware()) stack.Use(newRateLimitMiddleware()) handler := stack.Handler(myHandler)
This will create the following chain of calls: logging -> ratelimit -> myHandler