-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrouter.go
118 lines (89 loc) · 2.83 KB
/
router.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package router
import (
"net/http"
"slices"
"sync/atomic"
)
// router config
type HttpErrorHandler func(http.ResponseWriter, *http.Request, error)
type RouterConfig struct {
HandleError HttpErrorHandler
}
type RouterOption func(*RouterConfig)
func WithErrorHandler(fn HttpErrorHandler) RouterOption {
return func(cfg *RouterConfig) {
cfg.HandleError = fn
}
}
var routerCfg atomic.Pointer[RouterConfig]
func init() {
routerCfg.Store(&RouterConfig{HandleError: defaultHttpErrorHandler})
}
// An HTTP handler that allows for returning errors
type HttpHandler func(http.ResponseWriter, *http.Request) error
// ServeHTTP implements the http.Handler interface for HttpHandler
func (fn HttpHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if err := fn(w, req); err != nil {
opts := routerCfg.Load()
opts.HandleError(w, req, err)
}
}
// The default error handler
func defaultHttpErrorHandler(w http.ResponseWriter, req *http.Request, err error) {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
// https://gist.github.com/alexaandru/747f9d7bdfb1fa35140b359bf23fa820
type Middleware func(http.Handler) http.Handler
// Router is a simple HTTP router that wraps the standard library's ServeMux
type Router struct {
*http.ServeMux
chain []Middleware
}
func NewRouter(opts ...RouterOption) *Router {
ro := RouterConfig{
HandleError: defaultHttpErrorHandler,
}
for _, opt := range opts {
opt(&ro)
}
routerCfg.Store(&ro)
return &Router{ServeMux: &http.ServeMux{}, chain: []Middleware{}}
}
func (r *Router) Use(mx ...Middleware) {
r.chain = append(r.chain, mx...)
}
func (r *Router) Group(fn func(r *Router)) {
fn(&Router{ServeMux: r.ServeMux, chain: slices.Clone(r.chain)})
}
func (r *Router) Get(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodGet, path, fn, mx)
}
func (r *Router) Patch(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodPatch, path, fn, mx)
}
func (r *Router) Post(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodPost, path, fn, mx)
}
func (r *Router) Put(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodPut, path, fn, mx)
}
func (r *Router) Delete(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodDelete, path, fn, mx)
}
func (r *Router) Head(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodHead, path, fn, mx)
}
func (r *Router) Options(path string, fn HttpHandler, mx ...Middleware) {
r.handle(http.MethodOptions, path, fn, mx)
}
func (r *Router) handle(method, path string, fn HttpHandler, mx []Middleware) {
r.ServeMux.Handle(method+" "+path, r.wrap(fn, mx))
}
func (r *Router) wrap(fn HttpHandler, mx []Middleware) (out http.Handler) {
out, mx = http.Handler(fn), append(slices.Clone(r.chain), mx...)
slices.Reverse(mx)
for _, m := range mx {
out = m(out)
}
return
}