Middleware (Advanced)

This example will show how to create a more advanced version of middleware in Go.

A middleware in itself simply takes a http.HandlerFunc as one of its parameters, wraps it and returns a new http.HandlerFunc for the server to call.

Here we define a new type Middleware which makes it eventually easier to chain multiple middlewares together. This idea is inspired by Mat Ryers’ talk about Building APIs. You can find a more detailed explaination including the talk here.


This snippet explains in detail how a new middleware is created. In the full example below, we reduce this version by some boilerplate code.

  1. func createNewMiddleware() Middleware {
  2. // Create a new Middleware
  3. middleware := func(next http.HandlerFunc) http.HandlerFunc {
  4. // Define the http.HandlerFunc which is called by the server eventually
  5. handler := func(w http.ResponseWriter, r *http.Request) {
  6. // ... do middleware things
  7. // Call the next middleware/handler in chain
  8. next(w, r)
  9. }
  10. // Return newly created handler
  11. return handler
  12. }
  13. // Return newly created middleware
  14. return middleware
  15. }


This is the full example:

  1. // advanced-middleware.go
  2. package main
  3. import (
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "time"
  8. )
  9. type Middleware func(http.HandlerFunc) http.HandlerFunc
  10. // Logging logs all requests with its path and the time it took to process
  11. func Logging() Middleware {
  12. // Create a new Middleware
  13. return func(f http.HandlerFunc) http.HandlerFunc {
  14. // Define the http.HandlerFunc
  15. return func(w http.ResponseWriter, r *http.Request) {
  16. // Do middleware things
  17. start := time.Now()
  18. defer func() { log.Println(r.URL.Path, time.Since(start)) }()
  19. // Call the next middleware/handler in chain
  20. f(w, r)
  21. }
  22. }
  23. }
  24. // Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
  25. func Method(m string) Middleware {
  26. // Create a new Middleware
  27. return func(f http.HandlerFunc) http.HandlerFunc {
  28. // Define the http.HandlerFunc
  29. return func(w http.ResponseWriter, r *http.Request) {
  30. // Do middleware things
  31. if r.Method != m {
  32. http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
  33. return
  34. }
  35. // Call the next middleware/handler in chain
  36. f(w, r)
  37. }
  38. }
  39. }
  40. // Chain applies middlewares to a http.HandlerFunc
  41. func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
  42. for _, m := range middlewares {
  43. f = m(f)
  44. }
  45. return f
  46. }
  47. func Hello(w http.ResponseWriter, r *http.Request) {
  48. fmt.Fprintln(w, "hello world")
  49. }
  50. func main() {
  51. http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
  52. http.ListenAndServe(":8080", nil)
  53. }
  1. $ go run advanced-middleware.go
  2. 2017/02/11 00:34:53 / 0s
  3. $ curl -s http://localhost:8080/
  4. hello world
  5. $ curl -s -XPOST http://localhost:8080/
  6. Bad Request