Wrapping handler functions

We are going to utilize one of the most valuable patterns to learn when building services and websites in Go, something we already explored a little in Chapter 2, Adding User Accounts: wrapping handlers. We have seen how we can wrap http.Handler types to run code before and after our main handlers execute, and we are going to apply the same technique to http.HandlerFunc function alternatives.

API keys

Most web APIs require clients to register an API key for their application, which they are asked to send along with every request. Such keys have many purposes, ranging from simply identifying which app the requests are coming from to addressing authorization concerns in situations where some apps are only able to do limited things based on what a user has allowed. While we don't actually need to implement API keys for our application, we are going to ask clients to provide one, which will allow us to add an implementation later, while keeping the interface constant.

We are going to add our first HandlerFunc wrapper function called withAPIKey to the bottom of main.go:

func withAPIKey(fn http.HandlerFunc) http.HandlerFunc { 
  return func(w http.ResponseWriter, r *http.Request) { 
    key := r.URL.Query().Get("key") 
    if !isValidAPIKey(key) { 
      respondErr(w, r, http.StatusUnauthorized, "invalid
       API key") 
      return 
    } 
    ctx := context.WithValue(r.Context(),
     contextKeyAPIKey, key) 
    fn(w, r.WithContext(ctx)) 
  } 
} 

As you can see, our withAPIKey function both takes an http.HandlerFunc type as an argument and returns one; this is what we mean by wrapping in this context. The withAPIKey function relies on a number of other functions that we are yet to write, but you can clearly see what's going on. Our function immediately returns a new http.HandlerFunc type that performs a check for the key query parameter by calling isValidAPIKey. If the key is deemed invalid (by the return of false), we respond with an invalid API key error; otherwise, we put the key into the context and call the next handler. To use this wrapper, we simply pass an http.HandlerFunc type into this function in order to enable the key parameter check. Since it returns an http.HandlerFunc type too, the result can then be passed on to other wrappers or given directly to the http.HandleFunc function to actually register it as the handler for a particular path pattern.

Let's add our isValidAPIKey function next:

func isValidAPIKey(key string) bool { 
  return key == "abc123" 
} 

For now, we are simply going to hardcode the API key as abc123; anything else will return false and therefore be considered invalid. Later, we can modify this function to consult a configuration file or database to check the authenticity of a key without affecting how we use the isValidAPIKey method or the withAPIKey wrapper.

Cross-origin resource sharing

The same-origin security policy mandates that AJAX requests in web browsers be allowed only for services hosted on the same domain, which would make our API fairly limited since we won't necessarily be hosting all of the websites that use our web service. The CORS (Cross-origin resource sharing) technique circumnavigates the same-origin policy, allowing us to build a service capable of serving websites hosted on other domains. To do this, we simply have to set the Access-Control-Allow-Origin header in response to *. While we're at it, since we're going to use the Location header in our create poll call – we'll allow this header to be accessible by the client too, which can be done by listing it in the Access-Control-Expose-Headers header. Add the following code to main.go:

func withCORS(fn http.HandlerFunc) http.HandlerFunc { 
  return func(w http.ResponseWriter, r *http.Request) { 
    w.Header().Set("Access-Control-Allow-Origin", "*") 
    w.Header().Set("Access-Control-Expose-Headers",
     "Location") 
    fn(w, r) 
  } 
} 

This is the simplest wrapper function yet; it just sets the appropriate header on the ResponseWriter type and calls the specified http.HandlerFunc type.

Tip

In this chapter, we are handling CORS explicitly so we can understand exactly what is going on; for real production code, you should consider employing an open source solution, such as https://github.com/fasterness/cors.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.37.136