Middleware

Introduction

HTTP middleware provide a convenient mechanism for filtering HTTP requests entering your application. For example, gophersaurus includes a middleware that verifies that the client has a valid API key and whitelisted IP address. If the client does not provided a proper API key or is not a whitelisted IP, the middleware will not allow the request to proceed further into the application.

Of course, additional middleware can be written to perform a variety of tasks besides authentication. A CORS middleware might be responsible for adding the proper headers to all responses leaving your application. A logging middleware might log all incoming requests to your application.

All middleware is located in the /app/middleware directory.

Defining Middleware

Middleware has been designed to bridge the router and controllers packages. Middleware is defined in the middleware package located in the /app/middleware directory. The Middleware type represents a function that takes a Handler type and returns a Handler type. This taking and returns of a Handler type creates a chain of Middleware.

type Middleware func(http.Handler) http.Handler

To create a new middleware, add a .go file to the /app/middleware directory.

Below is an example middleware. In this middleware, we will only allow parameter values for age that are equal or greater than than 200. Otherwise, we will return an error message.

// Age describes a middleware that checks the age parameter
// for a value equal or greater than 200.
type Age struct {
  next http.Handler
}

// Do takes a handler and executes age middleware.
func (a Age) Do(h http.Handler) http.Handler {
	a.next = h
	return a
}

// ServeHTTP fulfills the http package interface for middleware.
func (a Age) ServeHTTP(resp http.Responder, req *http.Request) {

  // get the value of the age parameter
  age := req.Param("age")

  // convert age from a string type to an integer type
  if i, err := strconv.Atoi(age); err == nil {
    resp.WriteErrs(req, err.Error(), fmt.Sprintf("unable to convert '%s' to a string", age)
    return
  }

  // check the value of age is greater or equal to 200
  if i < 200 {
    resp.WriteErrs(req, "the value of age must be greater than 200")
    return
  }

  // execute the next middleware
  a.next.ServeHTTP(resp, req)
}

As you can see, if the given age is less than 200, the middleware will return an HTTP error to the client; otherwise, the request will be passed further into the application.

It’s best to envision middleware as a series of “layers” HTTP requests must pass through before they hit your application. Each layer can examine the request and even reject it entirely.