Skip to content

Commit ee09d7b

Browse files
committed
支持正则路由
1 parent 7016d69 commit ee09d7b

File tree

3 files changed

+118
-29
lines changed

3 files changed

+118
-29
lines changed

http/server/handler.go

Lines changed: 109 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package server
22

33
import (
4+
"context"
45
"fmt"
56
"net"
67
"net/http"
78
"reflect"
9+
"regexp"
810
"runtime/debug"
911
"strings"
1012
"sync"
@@ -18,12 +20,24 @@ var (
1820
server = newHTTPServer()
1921
)
2022

23+
type uriRegexp struct {
24+
path string
25+
keys []string
26+
exp *regexp.Regexp
27+
source reflect.Type
28+
}
29+
2130
// iface 对外服务接口, path格式:Method/URI
2231
type iface struct {
2332
path string
2433
source reflect.Type
2534
}
2635

36+
type prefix struct {
37+
path string
38+
uriExps []uriRegexp
39+
}
40+
2741
type httpServer struct {
2842
path map[string]iface
2943
prefix *btree.BTree
@@ -43,8 +57,8 @@ func newHTTPServer() *httpServer {
4357
// Filter 请求过滤, 如果返回结果为nil,直接返回,不再进行后续处理.
4458
type Filter func(http.ResponseWriter, *http.Request) *http.Request
4559

46-
func (i *iface) Less(bi btree.Item) bool {
47-
return strings.Compare(i.path, bi.(*iface).path) == 1
60+
func (p *prefix) Less(bi btree.Item) bool {
61+
return strings.Compare(p.path, bi.(*prefix).path) == 1
4862
}
4963

5064
//NameToPath 类名转路径
@@ -74,9 +88,40 @@ func RegisterPrefix(obj interface{}, path string) error {
7488
return register(obj, path, true)
7589
}
7690

77-
//RegisterPath 注册url完全匹配.
78-
func RegisterPath(obj interface{}, path string) error {
79-
return register(obj, path, true)
91+
var (
92+
keysExp *regexp.Regexp
93+
)
94+
95+
func init() {
96+
exp, err := regexp.Compile("{(\\w+)?}")
97+
if err != nil {
98+
panic(err.Error())
99+
}
100+
keysExp = exp
101+
}
102+
103+
func newURIRegexp(path string, source reflect.Type) uriRegexp {
104+
ur := uriRegexp{
105+
path: path,
106+
source: source,
107+
}
108+
109+
//log.Debugf("path:%v", path)
110+
for _, m := range keysExp.FindAllStringSubmatch(path, -1) {
111+
//log.Debugf("path:%v, key:%#v", path, m)
112+
ur.keys = append(ur.keys, m[1])
113+
}
114+
115+
np := keysExp.ReplaceAllString(path, "(.+)")
116+
exp, err := regexp.Compile(np)
117+
if err != nil {
118+
panic(err.Error())
119+
}
120+
ur.exp = exp
121+
122+
// log.Debugf("uriRegexp:%#v", ur)
123+
124+
return ur
80125
}
81126

82127
func register(obj interface{}, path string, isPrefix bool) error {
@@ -110,21 +155,37 @@ func register(obj interface{}, path string, isPrefix bool) error {
110155
continue
111156
}
112157

113-
ifc := iface{
114-
path: fmt.Sprintf("%v%v", method, path),
115-
source: rt.Elem(),
116-
}
117-
118158
//前缀匹配
119159
if isPrefix {
120-
if server.prefix.Has(&ifc) {
121-
panic(fmt.Sprintf("exist url:%v %v", method, path))
160+
p := prefix{
161+
path: fmt.Sprintf("%v%v", method, path),
162+
}
163+
164+
if idx := strings.Index(path, "{"); idx > 0 {
165+
p.path = fmt.Sprintf("%v%v", method, path[:idx])
166+
}
167+
168+
// log.Debugf("path:%v", p.path)
169+
170+
if server.prefix.Has(&p) {
171+
p = *(server.prefix.Get(&p).(*prefix))
122172
}
123-
server.prefix.ReplaceOrInsert(&ifc)
124-
log.Infof("add prefix %v %v %v", method, path, rt)
173+
174+
exp := newURIRegexp(path, rt.Elem())
175+
176+
p.uriExps = append(p.uriExps, exp)
177+
178+
server.prefix.ReplaceOrInsert(&p)
179+
180+
log.Infof("add prefix %v %v %v %v", method, exp.path, exp.keys, rt)
181+
125182
continue
126183
}
127184

185+
ifc := iface{
186+
path: fmt.Sprintf("%v%v", method, path),
187+
source: rt.Elem(),
188+
}
128189
//全路径匹配
129190
if _, ok := server.path[ifc.path]; ok {
130191
panic(fmt.Sprintf("exist url:%v %v", method, path))
@@ -144,27 +205,43 @@ func AddFilter(filter Filter) {
144205
server.filter = filter
145206
}
146207

147-
func getInterface(w http.ResponseWriter, r *http.Request) (i iface, ok bool) {
208+
func parseRequestValues(path string, ur uriRegexp) context.Context {
209+
ctx := context.Background()
210+
for i, v := range ur.exp.FindAllStringSubmatch(path, -1) {
211+
//log.Debugf("i:%v, v:%#v, keys:%#v", i, v, ur.keys)
212+
ctx = context.WithValue(ctx, ur.keys[i], v[1])
213+
}
214+
return ctx
215+
}
216+
217+
func getInterface(method, path string) (reflect.Type, context.Context) {
148218
server.RLock()
149219
defer server.RUnlock()
150220

151-
path := r.Method + r.URL.Path
221+
path = method + path
152222

153-
if i, ok = server.path[path]; ok {
223+
if i, ok := server.path[path]; ok {
154224
//log.Debugf("find path:%v", path)
155-
return
225+
return i.source, nil
156226
}
157227

228+
var p prefix
229+
var ok bool
158230
//如果完全匹配没找到,再找前缀的
159-
server.prefix.AscendGreaterOrEqual(&iface{path: path}, func(item btree.Item) bool {
160-
i = *(item.(*iface))
161-
ok = strings.HasPrefix(path, i.path)
162-
// log.Debugf("path:%v, ipath:%v, ok:%v", path, i.path, ok)
231+
server.prefix.AscendGreaterOrEqual(&prefix{path: path}, func(item btree.Item) bool {
232+
p = *(item.(*prefix))
233+
ok = strings.HasPrefix(path, p.path)
234+
//log.Debugf("path:%v, prefix:%v, ok:%v", path, p.path, ok)
163235
return !ok
164236
})
165237

166-
// log.Debugf("find prefix:%v, ok:%v", path, ok)
167-
return
238+
for _, ue := range p.uriExps {
239+
if ue.exp.MatchString(path) {
240+
// log.Debugf("uri exp:%v, path:%v", ue, path)
241+
return ue.source, parseRequestValues(path, ue)
242+
}
243+
}
244+
return nil, nil
168245
}
169246

170247
//ServeHTTP 真正对外服务接口
@@ -183,16 +260,20 @@ func (s *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
183260
return
184261
}
185262

186-
i, ok := getInterface(w, r)
187-
if !ok {
263+
tp, ctx := getInterface(r.Method, r.URL.Path)
264+
if tp == nil {
188265
log.Errorf("%v %v %v not found.", r.RemoteAddr, r.Method, r.URL)
189266
SendResponse(w, http.StatusNotFound, "invalid request")
190267
return
191268
}
192269

193-
log.Debugf("%v %v %v %v", r.RemoteAddr, r.Method, r.URL, i.source)
270+
if ctx != nil {
271+
nr = nr.WithContext(ctx)
272+
}
273+
274+
log.Debugf("%v %v %v %v", r.RemoteAddr, r.Method, r.URL, tp)
194275

195-
callback := reflect.New(i.source).MethodByName(r.Method).Interface().(func(http.ResponseWriter, *http.Request))
276+
callback := reflect.New(tp).MethodByName(r.Method).Interface().(func(http.ResponseWriter, *http.Request))
196277
callback(w, nr)
197278

198279
return

main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@ import (
1111
_ "github.com/dearcode/crab/server"
1212
)
1313

14+
type regexpTest struct {
15+
}
16+
17+
func (rt *regexpTest) GET(w http.ResponseWriter, req *http.Request) {
18+
fmt.Fprintf(w, "%#v", req.Context())
19+
}
20+
1421
type index struct {
1522
r *http.Request
1623
}
@@ -34,6 +41,7 @@ func main() {
3441
flag.Parse()
3542

3643
server.Register(&index{})
44+
server.RegisterPrefix(&regexpTest{}, "/regexp/{user}/test/")
3745

3846
go func() {
3947
for i := 0; i < 5; i++ {

server/init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ func init() {
88
server.RegisterPrefix(&staticServer{}, "/")
99
server.RegisterPrefix(&debugServer{}, "/debug/")
1010

11-
server.RegisterPath(&testServer{}, "/test/")
11+
server.RegisterPrefix(&testServer{}, "/test/")
1212

1313
server.Register(&user{})
1414
}

0 commit comments

Comments
 (0)