mirror of
https://github.com/therootcompany/golib.git
synced 2025-10-07 01:28:19 +00:00
3.0 KiB
3.0 KiB
http/middleware
A simple zero-cost middleware handler for Go's native net/http
ServeMux
.
(only 50 lines long, using only net/http
and slices
)
Turns tedious this:
mux.HandleFunc("GET /api/version", logRequests(timeRequests(getVersion)))
mux.HandleFunc("GET /api/data", logRequests(timeRequests(requireAuth(requireAdmin(getData)))))
Into organizable this:
mw := middleware.New(logRequests, timeRequests)
mux.HandleFunc("GET /api/version", mw.Handle(getVersion))
authMW := m.Use(requireAuth, requireAdmin)
mux.HandleFunc("GET /api/data", authMW.Handle(getData))
Using stdlib this:
type Middleware func(http.HandlerFunc) http.HandlerFunc
Zero-cost because each invocation of mv.Handle(handler)
composes the function calls exactly the same way as when done manually.
(the setup is done during route initialization and has no additional impact on requests)
Usage
- Create with
middleware.New(middlewares...)
- Extend with
mw.Use(midlewares...)
(copies and appends) - Apply with
specific.Handle(handler)
package main
import (
"net/http"
"github.com/therootcompany/golib/middleware"
)
func main() {
mux := http.NewServeMux()
mw := middleware.New(logRequests, basicAuth)
mux.HandleFunc("GET /api/data", mw.Handle(getData))
mux.HandleFunc("POST /api/data", mw.Handle(postData))
adminMW := mw.Use(requireAdmin)
mux.HandleFunc("DELETE /api/data", adminMW.Handle(deleteData))
http.ListenAndServe(":8080", mux)
}
Example Middleware
Middleware is any function that wraps and returns the built-in http.HandlerFunc
handler type.
type Middleware func(http.HandlerFunc) http.HandlerFunc
Example: Request logger
func logRequests(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
}
}
Example: Basic Auth Verifier, using envauth
import "github.com/therootcompany/golib/auth/envauth"
var creds = envauth.BasicCredentials{
Username: os.Getenv("BASIC_AUTH_USERNAME"),
Password: os.Getenv("BASIC_AUTH_PASSWORD"),
}
func basicAuth(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
if err := creds.Verify(user, pass); err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
}
}
Example: Admin role checker
func requireAdmin(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Assume JWT in context with roles
jws := r.Context().Value("jwt").(jwt.JWS)
if !slices.Contains(jws.Claims.Roles, "admin") {
http.Error(w, "Forbidden: Admin access required", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
}
}