📗
books
  • Introduction
  • Installation and Tools
    • Installation
    • Tools
  • Go basic knowledge
    • Go foundation
    • Control statements and functions
    • struct
    • Object-oriented
    • interface
    • Concurrency
  • General
    • Go Programming Basics
    • Web Programming Basics
  • Implementation
    • Basic web application
    • Designing our web app
    • Database Handling
    • Webapp Example
  • Form handling
    • Working with Forms
    • Uploading Files
  • Templates
  • User Authentication
  • Working with Files
  • Routing
  • Middleware
  • Building an API
  • Unit Testing
  • Version Control
  • Socket Programming
  • Contributors
Powered by GitBook
On this page
  • Example
  • Links

Was this helpful?

Middleware

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.

Links

PreviousRoutingNextBuilding an API

Last updated 4 years ago

Was this helpful?

- -

Previous section
Next section