-
-
Notifications
You must be signed in to change notification settings - Fork 24
/
method_handler.go
123 lines (105 loc) · 3.85 KB
/
method_handler.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
119
120
121
122
123
package muxie
import (
"net/http"
"strings"
)
// Methods returns a MethodHandler which caller can use
// to register handler for specific HTTP Methods inside the `Mux#Handle/HandleFunc`.
// Usage:
// mux := muxie.NewMux()
// mux.Handle("/user/:id", muxie.Methods().
// Handle("GET", getUserHandler).
// Handle("POST", saveUserHandler))
func Methods() *MethodHandler {
//
// Design notes, the latest one is selected:
//
// mux := muxie.NewMux()
//
// 1. mux.Handle("/user/:id", muxie.ByMethod("GET", getHandler).And/AndFunc("POST", postHandlerFunc))
//
// 2. mux.Handle("/user/:id", muxie.ByMethods{
// "GET": getHandler,
// "POST" http.HandlerFunc(postHandlerFunc),
// }) <- the only downside of this is that
// we lose the "Allow" header, which is not so important but it is RCF so we have to follow it.
//
// 3. mux.Handle("/user/:id", muxie.Method("GET", getUserHandler).Method("POST", saveUserHandler))
//
// 4. mux.Handle("/user/:id", muxie.Methods().
// Handle("GET", getHandler).
// HandleFunc("POST", postHandler))
//
return &MethodHandler{handlers: make(map[string]http.Handler)}
}
// NoContentHandler defaults to a handler which just sends 204 status.
// See `MethodHandler.NoContent` method.
var NoContentHandler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent)
})
// MethodHandler implements the `http.Handler` which can be used on `Mux#Handle/HandleFunc`
// to declare handlers responsible for specific HTTP method(s).
//
// Look `Handle` and `HandleFunc`.
type MethodHandler struct {
// origin *Mux
handlers map[string]http.Handler // method:handler
methodsAllowedStr string
}
// Handle adds a handler to be responsible for a specific HTTP Method.
// Returns this MethodHandler for further calls.
// Usage:
// Handle("GET", myGetHandler).HandleFunc("DELETE", func(w http.ResponseWriter, r *http.Request){[...]})
// Handle("POST, PUT", saveOrUpdateHandler)
// ^ can accept many methods for the same handler
// ^ methods should be separated by comma, comma following by a space or just space
func (m *MethodHandler) Handle(method string, handler http.Handler) *MethodHandler {
multiMethods := strings.FieldsFunc(method, func(c rune) bool {
return c == ',' || c == ' '
})
if len(multiMethods) > 1 {
for _, method := range multiMethods {
m.Handle(method, handler)
}
return m
}
method = normalizeMethod(method)
if m.methodsAllowedStr == "" {
m.methodsAllowedStr = method
} else {
m.methodsAllowedStr += ", " + method
}
m.handlers[method] = handler
return m
}
// NoContent registers a handler to a method
// which sends 204 (no status content) to the client.
//
// Example: _examples/11_cors for more.
func (m *MethodHandler) NoContent(methods ...string) *MethodHandler {
for _, method := range methods {
m.handlers[normalizeMethod(method)] = NoContentHandler
}
return m
}
// HandleFunc adds a handler function to be responsible for a specific HTTP Method.
// Returns this MethodHandler for further calls.
func (m *MethodHandler) HandleFunc(method string, handlerFunc func(w http.ResponseWriter, r *http.Request)) *MethodHandler {
m.Handle(method, http.HandlerFunc(handlerFunc))
return m
}
func (m *MethodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if handler, ok := m.handlers[r.Method]; ok {
handler.ServeHTTP(w, r)
return
}
// RCF rfc2616 https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
// The response MUST include an Allow header containing a list of valid methods for the requested resource.
//
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow#Examples
w.Header().Set("Allow", m.methodsAllowedStr)
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
}
func normalizeMethod(method string) string {
return strings.ToUpper(strings.TrimSpace(method))
}