mirror of
https://github.com/therootcompany/golib.git
synced 2025-10-07 01:28:19 +00:00
doc(http/middleware): describe and add examples
This commit is contained in:
parent
6ac8b08ecc
commit
56b2b43d3d
122
http/middleware/README.md
Normal file
122
http/middleware/README.md
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# 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:
|
||||||
|
|
||||||
|
```go
|
||||||
|
mux.HandleFunc("GET /api/version", logRequests(timeRequests(getVersion)))
|
||||||
|
mux.HandleFunc("GET /api/data", logRequests(timeRequests(requireAuth(requireAdmin(getData)))))
|
||||||
|
```
|
||||||
|
|
||||||
|
Into organizable this:
|
||||||
|
|
||||||
|
```go
|
||||||
|
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:
|
||||||
|
|
||||||
|
```go
|
||||||
|
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)`
|
||||||
|
|
||||||
|
```go
|
||||||
|
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.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Middleware func(http.HandlerFunc) http.HandlerFunc
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example: Request logger
|
||||||
|
|
||||||
|
```go
|
||||||
|
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`](https://github.com/therootcompany/golib/tree/main/auth/envauth)
|
||||||
|
|
||||||
|
```go
|
||||||
|
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
|
||||||
|
|
||||||
|
```go
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
Loading…
x
Reference in New Issue
Block a user