-
Notifications
You must be signed in to change notification settings - Fork 7
/
mux.go
332 lines (275 loc) · 9.52 KB
/
mux.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
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.
package mux
import (
"context"
"errors"
"net/http"
"strings"
"sync"
"github.com/issue9/middleware"
"github.com/issue9/mux/internal/tree"
"github.com/issue9/mux/params"
)
var (
defaultNotFound = func(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
}
defaultMethodNotAllowed = func(w http.ResponseWriter, r *http.Request) {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
}
)
// ErrNameExists 当为一个路由项命名时,若存在相同名称的,则返回此错误信息。
var ErrNameExists = errors.New("存在相同名称的路由项")
// Mux 提供了强大的路由匹配功能,可以对路径按正则或是请求方法进行匹配。
//
// 用法如下:
// m := mux.New()
// m.Get("/abc/h1", h1).
// Post("/abc/h2", h2).
// Handle("/api/{version:\\d+}",h3, http.MethodGet, http.MethodPost) // 只匹配 GET 和 POST
// http.ListenAndServe(m)
type Mux struct {
h *muxHandler
middlewares []middleware.Middleware
handler http.Handler
tree *tree.Tree
skipCleanPath bool
notFound http.HandlerFunc
methodNotAllowed http.HandlerFunc
// names 保存着路由项与其名称的对应关系,默认情况下,
// 路由项不存在名称,但可以通过 Mux.Name() 为其指定一个名称,
// 之后即可以在 Mux.URL() 使用名称来查找路由项。
names map[string]string
namesMu sync.RWMutex
}
type muxHandler struct {
mux *Mux
}
// New 声明一个新的 Mux。
//
// disableOptions 是否禁用自动生成 OPTIONS 功能;
// skipCleanPath 是否不对访问路径作处理,比如 "//api" ==> "/api";
// notFound 404 页面的处理方式,为 nil 时会调用默认的方式进行处理;
// methodNotAllowed 405 页面的处理方式,为 nil 时会调用默认的方式进行处理,
// 调用此方法前,会设置 Allow 报头,如果不需要,则要在 methodNotAllowed 中去掉。
func New(disableOptions, skipCleanPath bool, notFound, methodNotAllowed http.HandlerFunc) *Mux {
if notFound == nil {
notFound = defaultNotFound
}
if methodNotAllowed == nil {
methodNotAllowed = defaultMethodNotAllowed
}
mux := &Mux{
tree: tree.New(disableOptions),
names: make(map[string]string, 50),
skipCleanPath: skipCleanPath,
notFound: notFound,
methodNotAllowed: methodNotAllowed,
middlewares: make([]middleware.Middleware, 0, 10),
}
mux.h = &muxHandler{mux: mux}
mux.handler = mux.h
return mux
}
// Clean 清除所有的路由项
func (mux *Mux) Clean() *Mux {
mux.tree.Clean("")
return mux
}
// Remove 移除指定的路由项。
//
// 当未指定 methods 时,将删除所有 method 匹配的项。
// 指定错误的 methods 值,将自动忽略该值。
func (mux *Mux) Remove(pattern string, methods ...string) *Mux {
mux.tree.Remove(pattern, methods...)
return mux
}
// Handle 添加一条路由数据。
//
// pattern 为路由匹配模式,可以是正则匹配也可以是字符串匹配;
// methods 该路由项对应的请求方法,可通过 SupportedMethods() 获得当前支持的请求方法。
func (mux *Mux) Handle(pattern string, h http.Handler, methods ...string) error {
return mux.tree.Add(pattern, h, methods...)
}
// Options 将 OPTIONS 请求方法的报头 allow 值固定为指定的值。
//
// 若无特殊需求,不用调用此方法,系统会自动计算符合当前路由的请求方法列表。
// 如果想实现对处理方法的自定义,可以显示地调用 Handle 方法:
// Mux.Handle("/api/1", handle, http.MethodOptions)
func (mux *Mux) Options(pattern string, allow string) *Mux {
err := mux.tree.SetAllow(pattern, allow)
if err != nil {
panic(err)
}
return mux
}
func (mux *Mux) handle(pattern string, h http.Handler, methods ...string) *Mux {
if err := mux.Handle(pattern, h, methods...); err != nil {
panic(err)
}
return mux
}
// Get 相当于 Mux.Handle(pattern, h, http.MethodGet) 的简易写法
func (mux *Mux) Get(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h, http.MethodGet)
}
// Post 相当于 Mux.Handle(pattern, h, http.MethodPost) 的简易写法
func (mux *Mux) Post(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h, http.MethodPost)
}
// Delete 相当于 Mux.Handle(pattern, h, http.MethodDelete) 的简易写法
func (mux *Mux) Delete(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h, http.MethodDelete)
}
// Put 相当于 Mux.Handle(pattern, h, http.MethodPut) 的简易写法
func (mux *Mux) Put(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h, http.MethodPut)
}
// Patch 相当于 Mux.Handle(pattern, h, http.MethodPatch) 的简易写法
func (mux *Mux) Patch(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h, http.MethodPatch)
}
// Any 相当于 Mux.Handle(pattern, h) 的简易写法
func (mux *Mux) Any(pattern string, h http.Handler) *Mux {
return mux.handle(pattern, h)
}
// HandleFunc 功能同 Mux.Handle(),但是将第二个参数从 http.Handler 换成了 http.HandlerFunc
func (mux *Mux) HandleFunc(pattern string, fun http.HandlerFunc, methods ...string) error {
return mux.Handle(pattern, fun, methods...)
}
func (mux *Mux) handleFunc(pattern string, fun http.HandlerFunc, methods ...string) *Mux {
return mux.handle(pattern, fun, methods...)
}
// GetFunc 相当于 Mux.HandleFunc(pattern, func, http.MethodGet) 的简易写法
func (mux *Mux) GetFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun, http.MethodGet)
}
// PutFunc 相当于 Mux.HandleFunc(pattern, func, http.MethodPut) 的简易写法
func (mux *Mux) PutFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun, http.MethodPut)
}
// PostFunc 相当于 Mux.HandleFunc(pattern, func, "POST") 的简易写法
func (mux *Mux) PostFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun, http.MethodPost)
}
// DeleteFunc 相当于 Mux.HandleFunc(pattern, func, http.MethodDelete) 的简易写法
func (mux *Mux) DeleteFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun, http.MethodDelete)
}
// PatchFunc 相当于 Mux.HandleFunc(pattern, func, http.MethodPatch) 的简易写法
func (mux *Mux) PatchFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun, http.MethodPatch)
}
// AnyFunc 相当于 Mux.HandleFunc(pattern, func) 的简易写法
func (mux *Mux) AnyFunc(pattern string, fun http.HandlerFunc) *Mux {
return mux.handleFunc(pattern, fun)
}
// AddMiddlewares 添加中间件,可多次调用。
//
// Deprecated: 采用 AppendMiddlewares 代替
func (mux *Mux) AddMiddlewares(m ...middleware.Middleware) *Mux {
return mux.AppendMiddlewares(m...)
}
// AppendMiddlewares 添加中间件,可多次调用。
func (mux *Mux) AppendMiddlewares(m ...middleware.Middleware) *Mux {
mux.middlewares = append(mux.middlewares, m...)
mux.handler = middleware.Handler(mux.h, mux.middlewares...)
return mux
}
// UnshiftMiddlewares 前排插入中间件。可多次调用
func (mux *Mux) UnshiftMiddlewares(m ...middleware.Middleware) *Mux {
ms := make([]middleware.Middleware, len(m)+len(mux.middlewares))
ms = append(ms, m...)
ms = append(ms, mux.middlewares...)
mux.middlewares = ms
mux.handler = middleware.Handler(mux.h, mux.middlewares...)
return mux
}
// ResetMiddlewares 清除中间件。
func (mux *Mux) ResetMiddlewares() *Mux {
mux.middlewares = mux.middlewares[:0]
mux.handler = mux.h
return mux
}
func (mux *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
mux.handler.ServeHTTP(w, r)
}
func (mh *muxHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
p := r.URL.Path
if !mh.mux.skipCleanPath {
p = cleanPath(p)
}
hs, ps := mh.mux.tree.Handler(p)
if hs == nil {
mh.mux.notFound(w, r)
return
}
h := hs.Handler(r.Method)
if h == nil {
w.Header().Set("Allow", hs.Options())
mh.mux.methodNotAllowed(w, r)
return
}
if len(ps) > 0 {
ctx := context.WithValue(r.Context(), params.ContextKeyParams, ps)
r = r.WithContext(ctx)
}
h.ServeHTTP(w, r)
}
// Name 为一条路由项命名。
// URL 可以通过此属性来生成地址。
func (mux *Mux) Name(name, pattern string) error {
mux.namesMu.Lock()
defer mux.namesMu.Unlock()
if _, found := mux.names[name]; found {
return ErrNameExists
}
mux.names[name] = pattern
return nil
}
// URL 根据参数生成地址。
// name 为路由的名称,或是直接为路由项的定义内容;
// params 为路由项中的参数,键名为参数名,键值为参数值。
func (mux *Mux) URL(name string, params map[string]string) (string, error) {
mux.namesMu.RLock()
pattern, found := mux.names[name]
mux.namesMu.RUnlock()
if !found {
pattern = name
}
return mux.tree.URL(pattern, params)
}
// Params 获取路由的参数集合。详细情况可参考 params.Get
func Params(r *http.Request) params.Params {
return params.Get(r)
}
// 清除路径中的重复的 / 字符
func cleanPath(p string) string {
if p == "" {
return "/"
}
if p[0] != '/' {
p = "/" + p
}
index := strings.Index(p, "//")
if index == -1 {
return p
}
pp := make([]byte, index+1, len(p))
copy(pp, p[:index+1])
slash := true
for i := index + 2; i < len(p); i++ {
if p[i] == '/' {
if slash {
continue
}
slash = true
} else {
slash = false
}
pp = append(pp, p[i])
}
return string(pp)
}