Middlewares

Middleware is really anything that extends your webapp in a modular way. Most common examples are probably parsing the request parameters/body and storing them in an easily-accessible format so you don't have to do it in every single handler, or session handling as we mentioned in the previous chapter. Other examples could be throttling or IP filtering, which would also happen before you start building your response, or compression, which would happen after you've built your response.

//RequiresLogin is a middleware which will be used for each 
//httpHandler to check if there is any active session
func RequiresLogin(handler func(w http.ResponseWriter, r *http.Request)) 
    func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        if !sessions.IsLoggedIn(r) {
            http.Redirect(w, r, "/login/", 302)
            return
        }
        handler(w, r)
    }
}

The above function counts as middleware - it doesn't know anything about your app except how you handle sessions. If you're not logged in, it redirects, otherwise it doesn't do anything and passes along to the next handler. That next handler might be where you actually build your response, or it could be another middleware component that does something else first.

To know someone's logged in, yes, you want to create a session identifier and store that somewhere on the server side (in memory or a database) and also set it in the user's cookies. Your session IDs should be sufficiently random and long that they couldn't be easily guessed. I think a common way of satisfying that is creating a UUID and then base64 encode that. Or, you could just generate a bunch of random bytes.

To know which user is logged in, the session ID should be the key that maps to a user ID. So, you'd make a map of Session ID => User ID, or something similar in your database.

Then, before every request, you'd

  1. Check user's cookies for Session ID. If none, user is not logged in.
  2. Check your store for user's Session ID. If it's not found, then it's invalid - user is not logged in.
  3. If you found it, use it to look up the user's ID. User is now logged in.
  4. Now you've got the user ID and can use it as a filter when querying your DB if you only want to show that user's tasks.

Example

Without middleware:

//IsLoggedIn will check if the user has an active session and return True
func IsLoggedIn(r *http.Request) bool {
    session, _ := Store.Get(r, "session")
    if session.Values["loggedin"] == "true" {
        return true
    }
    return false
}


//SearchTaskFunc is used to handle the /search/ url, handles the search function
func SearchTaskFunc(w http.ResponseWriter, r *http.Request) {
    if sessions.IsLoggedin(r) {
        if r.Method == "POST" {
            r.ParseForm()
            query := r.Form.Get("query")
            context := db.SearchTask(query)
            categories := db.GetCategories()
            context.Categories = categories
            searchTemplate.Execute(w, context)
        }
    } else {
        http.Redirect(w, r, "/login/", 302)    
    }

With Middleware:

http.HandleFunc("/", views.RequiresLogin(views.ShowAllTasksFunc))

//SearchTaskFunc is used to handle the /search/ url, 
//handles the search function
func SearchTaskFunc(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        r.ParseForm()
        query := r.Form.Get("query")
        context := db.SearchTask(query)
        categories := db.GetCategories()
        context.Categories = categories
        searchTemplate.Execute(w, context)
        }
}

This way, we do not have to repeat the if sessions.IsLoggedin() block in each of our view which requires authentication. In this example we have used it for session handling, but it can be used for any purpose which requires some kind of pre handling of any view.

-Previous section -Next section

results matching ""

    No results matching ""