-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjano.go
More file actions
133 lines (116 loc) · 3.61 KB
/
Copy pathjano.go
File metadata and controls
133 lines (116 loc) · 3.61 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
package jano
import (
"context"
"net/http"
"strings"
)
// Jano struct to hold the routes and middlewares
type Jano struct {
routes map[string]map[string]http.HandlerFunc
middlewares []func(http.Handler) http.Handler
notFound http.HandlerFunc
}
// New creates a new instance of Jano
func New() *Jano {
return &Jano{
routes: make(map[string]map[string]http.HandlerFunc),
middlewares: []func(http.Handler) http.Handler{},
notFound: http.NotFound,
}
}
// Get registers a GET handler
func (j *Jano) Get(path string, handler http.HandlerFunc) {
j.addRoute("GET", path, handler)
}
// Post registers a POST handler
func (j *Jano) Post(path string, handler http.HandlerFunc) {
j.addRoute("POST", path, handler)
}
// Put registers a PUT handler
func (j *Jano) Put(path string, handler http.HandlerFunc) {
j.addRoute("PUT", path, handler)
}
// Delete registers a DELETE handler
func (j *Jano) Delete(path string, handler http.HandlerFunc) {
j.addRoute("DELETE", path, handler)
}
// Patch registers a PATCH handler
func (j *Jano) Patch(path string, handler http.HandlerFunc) {
j.addRoute("PATCH", path, handler)
}
// Options registers an OPTIONS handler
func (j *Jano) Options(path string, handler http.HandlerFunc) {
j.addRoute("OPTIONS", path, handler)
}
// Head registers a HEAD handler
func (j *Jano) Head(path string, handler http.HandlerFunc) {
j.addRoute("HEAD", path, handler)
}
// Use adds a middleware to the chain
func (j *Jano) Use(middleware func(http.Handler) http.Handler) {
j.middlewares = append(j.middlewares, middleware)
}
// addRoute adds a handler for a specific method and path
func (j *Jano) addRoute(method, path string, handler http.HandlerFunc) {
if j.routes[path] == nil {
j.routes[path] = make(map[string]http.HandlerFunc)
}
j.routes[path][method] = handler
}
// Router returns the http.Handler to be used by http.Server
func (j *Jano) Router() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handler, params, ok := j.findHandler(r.Method, r.URL.Path)
if !ok {
j.notFound(w, r)
return
}
r = setParams(r, params)
wrappedHandler := http.Handler(handler)
for i := len(j.middlewares) - 1; i >= 0; i-- {
wrappedHandler = j.middlewares[i](wrappedHandler)
}
wrappedHandler.ServeHTTP(w, r)
})
}
// findHandler finds the handler for the given method and path
func (j *Jano) findHandler(method, path string) (http.HandlerFunc, map[string]string, bool) {
for route, methodHandlers := range j.routes {
if params, ok := matchRoute(route, path); ok {
if handler, ok := methodHandlers[method]; ok {
return handler, params, true
}
}
}
return nil, nil, false
}
// NotFound sets the custom 404 handler
func (j *Jano) NotFound(handler http.HandlerFunc) {
j.notFound = handler
}
// matchRoute checks if the route matches the path and extracts parameters
func matchRoute(route, path string) (map[string]string, bool) {
routeParts := strings.Split(route, "/")
pathParts := strings.Split(path, "/")
if len(routeParts) != len(pathParts) {
return nil, false
}
params := make(map[string]string)
for i := range routeParts {
if strings.HasPrefix(routeParts[i], "{") && strings.HasSuffix(routeParts[i], "}") {
paramName := routeParts[i][1:len(routeParts[i])-1]
params[paramName] = pathParts[i]
} else if routeParts[i] != pathParts[i] {
return nil, false
}
}
return params, true
}
// setParams adds the parameters to the request context
func setParams(r *http.Request, params map[string]string) *http.Request {
ctx := r.Context()
for key, value := range params {
ctx = context.WithValue(ctx, key, value)
}
return r.WithContext(ctx)
}