Sling Academy
Home/Golang/Creating HTTP Middleware in Go for Reusable Utilities

Creating HTTP Middleware in Go for Reusable Utilities

Last updated: November 27, 2024

Middleware in an HTTP server in Go allows you to wrap the logic of your requests with reusable utilities, such as logging, authentication, or response modification. This helps in maintaining a cleaner and more organized codebase by separating concerns and adhering to the DRY (Don't Repeat Yourself) principle.

What is Middleware?

Middleware acts as a chain of handlers which are executed sequentially prior to your main request handler. Each middleware performs its duties before or after calling the next middleware in the chain.

Here is a basic example: consider an HTTP server where we want to log every request and ensure all endpoints are secured.

Creating a Simple Middleware in Go

If you're building an HTTP server using Go's standard library, you typically define a function that matches the signature:


func(yourHandler http.HandlerFunc) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    // Do something before calling the handler, e.g., logging
    yourHandler.ServeHTTP(w, r)  
    // Do something after calling the handler, e.g., metrics collection
  }
}

Let's start by setting up a logging middleware:


package main

import (
  "fmt"
  "log"
  "net/http"
  "time"
)

// Logger middleware logs each HTTP request.
func Logger(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    next.ServeHTTP(w, r)
    log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
  })
}

func HelloHandler(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Hello, World!"))
}

func main() {
  http.Handle("/", Logger(http.HandlerFunc(HelloHandler)))
  fmt.Println("Starting server on :8080")
  http.ListenAndServe(":8080", nil)
}

Adding Authentication Middleware

Let's add another piece of middleware for enforcing basic authentication:


func BasicAuth(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    user, pass, ok := r.BasicAuth()
    if !ok || user != "admin" || pass != "admin" {
      w.WriteHeader(http.StatusUnauthorized)
      w.Write([]byte("Unauthorized"))
      return
    }
    next.ServeHTTP(w, r)
  })
}

func main() {
  http.Handle("/", Logger(BasicAuth(http.HandlerFunc(HelloHandler))))
  fmt.Println("Starting server on :8080")
  http.ListenAndServe(":8080", nil)
}

Chaining Multiple Middlewares

You can keep chaining as many middleware functions as you need. Consider the sequence of calling them and ensure that each one's responsibility is clear and independent.

Conclusion

By using middleware in Go, you can efficiently manage cross-cutting concerns, maximize code reusability, and maintain a cleaner code structure. This pattern is particularly useful in web application development using Go's robust standard libraries.

Next Article: Understanding `runtime` Package for Low-Level Go Utilities

Previous Article: Dynamic Code Execution with `plugin` Package in Go

Series: Go Utilities and Tools

Golang

Related Articles

You May Also Like

  • How to remove HTML tags in a string in Go
  • How to remove special characters in a string in Go
  • How to remove consecutive whitespace in a string in Go
  • How to count words and characters in a string in Go
  • Relative imports in Go: Tutorial & Examples
  • How to run Python code with Go
  • How to generate slug from title in Go
  • How to create an XML sitemap in Go
  • How to redirect in Go (301, 302, etc)
  • Using Go with MongoDB: CRUD example
  • Auto deploy Go apps with CI/ CD and GitHub Actions
  • Fixing Go error: method redeclared with different receiver type
  • Fixing Go error: copy argument must have slice type
  • Fixing Go error: attempted to use nil slice
  • Fixing Go error: assignment to constant variable
  • Fixing Go error: cannot compare X (type Y) with Z (type W)
  • Fixing Go error: method has pointer receiver, not called with pointer
  • Fixing Go error: assignment mismatch: X variables but Y values
  • Fixing Go error: array index must be non-negative integer constant