mirror of
https://github.com/therootcompany/golib.git
synced 2026-01-27 23:18:05 +00:00
ref!(http/middleware): bump to v2 to use http.Handler rather than http.HandlerFunc
This commit is contained in:
parent
8136b7f4b9
commit
b559a25404
@ -8,8 +8,8 @@ A simple zero-cost middleware handler for Go's native `net/http` `ServeMux`. \
|
|||||||
Turns tedious this:
|
Turns tedious this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
mux.HandleFunc("GET /api/version", logRequests(timeRequests(getVersion)))
|
mux.Handle("GET /api/version", logRequests(timeRequests(http.HandlerFunc(getVersion))))
|
||||||
mux.HandleFunc("GET /api/data", logRequests(timeRequests(requireAuth(requireAdmin(getData)))))
|
mux.Handle("GET /api/data", logRequests(timeRequests(requireAuth(requireAdmin(http.HandlerFunc(getData))))))
|
||||||
```
|
```
|
||||||
|
|
||||||
Into organizable this:
|
Into organizable this:
|
||||||
@ -25,7 +25,7 @@ authMW.HandleFunc("GET /api/data", getData)
|
|||||||
Using stdlib this:
|
Using stdlib this:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Middleware func(http.HandlerFunc) http.HandlerFunc
|
type Middleware func(http.Handler) http.Handler
|
||||||
```
|
```
|
||||||
|
|
||||||
**Zero-cost** because each invocation of `mv.Handle(handler)` composes the function calls _exactly_ the same way as when done manually. \
|
**Zero-cost** because each invocation of `mv.Handle(handler)` composes the function calls _exactly_ the same way as when done manually. \
|
||||||
@ -49,12 +49,12 @@ import (
|
|||||||
func main() {
|
func main() {
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
mw := middleware.New(logRequests, basicAuth)
|
mw := middleware.WithMux(mux, recoverPanics, logRequests, basicAuth)
|
||||||
mux.HandleFunc("GET /api/data", mw.Handle(getData))
|
mux.HandleFunc("GET /api/data", getData)
|
||||||
mux.HandleFunc("POST /api/data", mw.Handle(postData))
|
mux.HandleFunc("POST /api/data", postData)
|
||||||
|
|
||||||
adminMW := mw.Use(requireAdmin)
|
adminMW := mw.Use(requireAdmin)
|
||||||
mux.HandleFunc("DELETE /api/data", adminMW.Handle(deleteData))
|
adminMW.HandleFunc("DELETE /api/data", deleteData)
|
||||||
|
|
||||||
http.ListenAndServe(":8080", mux)
|
http.ListenAndServe(":8080", mux)
|
||||||
}
|
}
|
||||||
@ -62,16 +62,37 @@ func main() {
|
|||||||
|
|
||||||
### Example Middleware
|
### Example Middleware
|
||||||
|
|
||||||
Middleware is any function that wraps and returns the built-in `http.HandlerFunc` handler type.
|
Middleware is any function that wraps and returns the built-in `http.Handler` handler type.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Middleware func(http.HandlerFunc) http.HandlerFunc
|
type Middleware func(http.Handler) http.Handler
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example: Panic handler
|
||||||
|
|
||||||
|
```go
|
||||||
|
func recoverPanics(next http.Handler) http.Handler {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
defer func() {
|
||||||
|
if _err := recover(); _err != nil {
|
||||||
|
err, ok := _err.(error)
|
||||||
|
if !ok {
|
||||||
|
err = fmt.Errorf("%v", _err)
|
||||||
|
}
|
||||||
|
api.InternalError(w, r, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example: Request logger
|
#### Example: Request logger
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func logRequests(next http.HandlerFunc) http.HandlerFunc {
|
func logRequests(next http.Handler) http.Handler {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
@ -91,7 +112,7 @@ var creds = envauth.BasicCredentials{
|
|||||||
Password: os.Getenv("BASIC_AUTH_PASSWORD"),
|
Password: os.Getenv("BASIC_AUTH_PASSWORD"),
|
||||||
}
|
}
|
||||||
|
|
||||||
func basicAuth(next http.HandlerFunc) http.HandlerFunc {
|
func basicAuth(next http.Handler) http.Handler {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
user, pass, _ := r.BasicAuth()
|
user, pass, _ := r.BasicAuth()
|
||||||
@ -108,7 +129,7 @@ func basicAuth(next http.HandlerFunc) http.HandlerFunc {
|
|||||||
#### Example: Admin role checker
|
#### Example: Admin role checker
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func requireAdmin(next http.HandlerFunc) http.HandlerFunc {
|
func requireAdmin(next http.Handler) http.Handler {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
// Assume JWT in context with roles
|
// Assume JWT in context with roles
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
module github.com/therootcompany/golib/http/middleware
|
module github.com/therootcompany/golib/http/middleware/v2
|
||||||
|
|
||||||
go 1.24.6
|
go 1.24.6
|
||||||
|
|||||||
@ -16,21 +16,22 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Middleware receives and returns and http.HandlerFunc
|
// Muxer can mux Handlers and HandleFuncs
|
||||||
type Middleware func(http.HandlerFunc) http.HandlerFunc
|
type Muxer interface {
|
||||||
|
Handle(path string, handler http.Handler)
|
||||||
|
HandleFunc(path string, handle func(w http.ResponseWriter, r *http.Request))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Middleware receives and returns and http.Handler
|
||||||
|
type Middleware func(http.Handler) http.Handler
|
||||||
|
|
||||||
// MiddlewareChain enables inline chaining
|
// MiddlewareChain enables inline chaining
|
||||||
type MiddlewareChain struct {
|
type MiddlewareChain struct {
|
||||||
middlewares []Middleware
|
middlewares []Middleware
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a reusable MiddlewareChain with 0 or more middleware
|
|
||||||
func New(middlewares ...Middleware) MiddlewareChain {
|
|
||||||
return MiddlewareChain{middlewares: middlewares}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle composes middleware with the final handler
|
// Handle composes middleware with the final handler
|
||||||
func (c MiddlewareChain) Handle(handler http.HandlerFunc) http.HandlerFunc {
|
func (c MiddlewareChain) Handle(handler http.Handler) http.Handler {
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
panic("mw.New(...).Use(...).Handle(-->this<--) requires a handler")
|
panic("mw.New(...).Use(...).Handle(-->this<--) requires a handler")
|
||||||
}
|
}
|
||||||
@ -48,20 +49,6 @@ func (c MiddlewareChain) Handle(handler http.HandlerFunc) http.HandlerFunc {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use appends additional middleware to the chain
|
|
||||||
func (c MiddlewareChain) Use(middlewares ...Middleware) MiddlewareChain {
|
|
||||||
newMiddlewares := make([]Middleware, len(c.middlewares), len(c.middlewares)+len(middlewares))
|
|
||||||
copy(newMiddlewares, c.middlewares)
|
|
||||||
newMiddlewares = append(newMiddlewares, middlewares...)
|
|
||||||
|
|
||||||
return MiddlewareChain{middlewares: newMiddlewares}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Muxer interface {
|
|
||||||
Handle(path string, handler http.Handler)
|
|
||||||
HandleFunc(path string, handle func(w http.ResponseWriter, r *http.Request))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MiddlewareMux enables inline chaining
|
// MiddlewareMux enables inline chaining
|
||||||
type MiddlewareMux struct {
|
type MiddlewareMux struct {
|
||||||
middlewares []Middleware
|
middlewares []Middleware
|
||||||
@ -89,15 +76,15 @@ func (c MiddlewareMux) With(middlewares ...Middleware) MiddlewareMux {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c MiddlewareMux) Handle(path string, handler http.Handler) {
|
func (c MiddlewareMux) Handle(path string, handler http.Handler) {
|
||||||
c.mux.Handle(path, c.handle(handler.ServeHTTP))
|
c.mux.Handle(path, c.handle(handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c MiddlewareMux) HandleFunc(path string, handler http.HandlerFunc) {
|
func (c MiddlewareMux) HandleFunc(path string, handler http.HandlerFunc) {
|
||||||
c.mux.HandleFunc(path, c.handle(handler))
|
c.mux.Handle(path, c.handle(handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle composes middleware with the final handler
|
// Handle composes middleware with the final handler
|
||||||
func (c MiddlewareMux) handle(handler http.HandlerFunc) http.HandlerFunc {
|
func (c MiddlewareMux) handle(handler http.Handler) http.Handler {
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
panic("mw.New(...).Use(...).Handle(-->this<--) requires a handler")
|
panic("mw.New(...).Use(...).Handle(-->this<--) requires a handler")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user