-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrouter.go
More file actions
187 lines (159 loc) · 6.85 KB
/
router.go
File metadata and controls
187 lines (159 loc) · 6.85 KB
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package httpx
import (
"context"
"net/http"
)
// HandlerFunc is an HTTP handler that returns an error.
// This allows handlers to return errors which can be handled centrally by an ErrorHandlerFunc.
type HandlerFunc func(w http.ResponseWriter, r *http.Request) error
// LoggingFunc is a function that wraps an http.Handler to add logging functionality.
// It follows the standard middleware pattern for http.Handler.
type LoggingFunc func(http.Handler) http.Handler
// HTTPMiddleware is a standard HTTP middleware that wraps an http.Handler.
// Unlike HandlerFunc middleware which is part of the route chain and returns errors,
// HTTPMiddleware runs before routing and follows the standard middleware pattern.
type HTTPMiddleware func(http.Handler) http.Handler
// ErrorHandlerFunc converts a HandlerFunc (which returns an error) into a standard http.HandlerFunc.
// It is responsible for handling any errors returned by the HandlerFunc and writing appropriate HTTP responses.
type ErrorHandlerFunc func(HandlerFunc) http.HandlerFunc
type contextKey string
const nextHandlerKey contextKey = "nextHandler"
// Next calls the next handler in the middleware chain.
// Middleware should call Next to continue the request processing.
// If there is no next handler, Next returns nil.
//
// Example:
//
// func MyMiddleware(w http.ResponseWriter, r *http.Request) error {
// // Do something before
// err := httpx.Next(w, r)
// // Do something after
// return err
// }
func Next(w http.ResponseWriter, r *http.Request) error {
if next, ok := r.Context().Value(nextHandlerKey).(HandlerFunc); ok {
return next(w, r)
}
// No more handlers in chain
return nil
}
// Group represents a router group that can have a prefix, middlewares, and sub-groups.
// It implements http.Handler and can be used directly with http.Server.
type Group struct {
errorHandler ErrorHandlerFunc
prefix string
mux *http.ServeMux
middlewares []HandlerFunc
logger LoggingFunc
}
// NewRouter creates a new root router with optional global middlewares and logger.
// The logger wraps all routes for logging HTTP requests and responses.
// The errorHandler converts HandlerFunc errors into HTTP responses.
func NewRouter(logger LoggingFunc, errorHandler ErrorHandlerFunc, ms ...HandlerFunc) *Group {
if logger == nil {
logger = defaultLogger
}
if errorHandler == nil {
errorHandler = defaultErrorHandler
}
return &Group{
errorHandler: errorHandler,
mux: http.NewServeMux(),
middlewares: ms,
logger: logger,
}
}
// Group creates a new subgroup with a prefix and optional middlewares.
// The subgroup inherits the parent's mux, logger, and errorHandler.
// Middlewares from the parent group are combined with the new middlewares.
func (g *Group) Group(prefix string, ms ...HandlerFunc) *Group {
return &Group{
prefix: g.prefix + prefix,
mux: g.mux,
middlewares: append(g.middlewares, ms...),
logger: g.logger,
errorHandler: g.errorHandler,
}
}
// registerRoute registers a route with the given HTTP method, path, and handlers.
// The handlers list can include zero or more middlewares followed by a final handler.
// Middlewares are executed in the order they appear, and each must call Next() to continue.
func (g *Group) registerRoute(method, path string, handlers ...HandlerFunc) {
if len(handlers) == 0 {
panic("at least one handler is required")
}
// Combine: global middlewares + group middlewares + route handlers
allHandlers := append(g.middlewares, handlers...)
var chain HandlerFunc
// Build the chain from end to start
// Start with the last handler (no next handler after it)
for i := len(allHandlers) - 1; i >= 0; i-- {
next := chain
handler := allHandlers[i]
chain = func(h HandlerFunc, n HandlerFunc) HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) error {
// Store the next handler in context
ctx := context.WithValue(r.Context(), nextHandlerKey, n)
r = r.WithContext(ctx)
return h(w, r)
}
}(handler, next)
}
fullPath := method + " " + g.prefix + path
g.mux.Handle(fullPath, g.logger(g.errorHandler(chain)))
}
// GET registers a GET route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) GET(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodGet, path, handlers...)
}
// HEAD registers a HEAD route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) HEAD(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodHead, path, handlers...)
}
// POST registers a POST route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) POST(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodPost, path, handlers...)
}
// PUT registers a PUT route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) PUT(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodPut, path, handlers...)
}
// PATCH registers a PATCH route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) PATCH(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodPatch, path, handlers...)
}
// DELETE registers a DELETE route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) DELETE(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodDelete, path, handlers...)
}
// CONNECT registers a CONNECT route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) CONNECT(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodConnect, path, handlers...)
}
// OPTIONS registers a OPTIONS route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) OPTIONS(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodOptions, path, handlers...)
}
// TRACE registers a TRACE route with optional middlewares and a final handler.
// Middlewares are executed in the order provided, followed by the final handler.
func (g *Group) TRACE(path string, handlers ...HandlerFunc) {
g.registerRoute(http.MethodTrace, path, handlers...)
}
// ServeHTTP implements the http.Handler interface.
// This allows Group to be used directly with http.Server.
//
// Example:
//
// router := httpx.NewRouter(nil, nil)
// http.ListenAndServe(":8080", router)
func (g *Group) ServeHTTP(w http.ResponseWriter, r *http.Request) {
g.mux.ServeHTTP(w, r)
}