M mux.go => mux.go +6 -2
@@ 145,12 145,15 @@ func (mux *ServeMux) handler(r *http.Request) (http.Handler, *http.Request) {
return node.handler, r
}
+ offset := uint(1)
+
nodeloop:
for node != nil {
// If this is a variable route,
if len(node.child) == 1 && node.child[0].typ != typStatic {
var part, remain string
- part, remain, r = node.child[0].match(path, r)
+ part, remain, r = node.child[0].match(path, offset, r)
+ offset += uint(len(part)) + 1
// If the type doesn't match, we're done.
if part == "" {
@@ 173,7 176,8 @@ nodeloop:
// If this is a static route
for _, child := range node.child {
var part, remain string
- part, remain, r = child.match(path, r)
+ part, remain, r = child.match(path, offset, r)
+ offset += uint(len(part)) + 1
// The child did not match, so check the next.
if part == "" {
path = remain
M node.go => node.go +13 -8
@@ 14,7 14,7 @@ type node struct {
child []node
}
-func (n *node) match(path string, r *http.Request) (part string, remain string, req *http.Request) {
+func (n *node) match(path string, offset uint, r *http.Request) (part string, remain string, req *http.Request) {
// Nil nodes never match.
if n == nil {
return "", "", r
@@ 23,7 23,7 @@ func (n *node) match(path string, r *http.Request) (part string, remain string,
// wildcards are a special case that always match the entire remainder of the
// path.
if n.typ == typWild {
- r = addValue(r, n.name, path)
+ r = addValue(r, n.name, path, offset, path)
return path, "", r
}
@@ 35,36 35,41 @@ func (n *node) match(path string, r *http.Request) (part string, remain string,
}
return "", path, r
case typString:
- r = addValue(r, n.name, part)
+ r = addValue(r, n.name, part, offset, part)
return part, remain, r
case typUint:
v, err := strconv.ParseUint(part, 10, 64)
if err != nil {
return "", path, r
}
- r = addValue(r, n.name, v)
+ r = addValue(r, n.name, part, offset, v)
return part, remain, r
case typInt:
v, err := strconv.ParseInt(part, 10, 64)
if err != nil {
return "", path, r
}
- r = addValue(r, n.name, v)
+ r = addValue(r, n.name, part, offset, v)
return part, remain, r
case typFloat:
v, err := strconv.ParseFloat(part, 64)
if err != nil {
return "", path, r
}
- r = addValue(r, n.name, v)
+ r = addValue(r, n.name, part, offset, v)
return part, remain, r
}
panic("unknown type")
}
-func addValue(r *http.Request, name string, val interface{}) *http.Request {
+func addValue(r *http.Request, name, raw string, offset uint, val interface{}) *http.Request {
+ pinfo := ParamInfo{
+ Value: val,
+ Raw: raw,
+ Offset: offset,
+ }
if name != "" {
- return r.WithContext(context.WithValue(r.Context(), ctxParam(name), val))
+ return r.WithContext(context.WithValue(r.Context(), ctxParam(name), pinfo))
}
return r
}
M params.go => params.go +17 -2
@@ 4,7 4,22 @@ import (
"net/http"
)
+// ParamInfo represents a route parameter and related metadata.
+type ParamInfo struct {
+ // The parsed value of the parameter (for example int64(10))
+ Value interface{}
+ // The raw value of the parameter (for example "10")
+ Raw string
+ // The offset in the path where this parameter was found (for example if "10"
+ // is parsed out of the path "/10" the offset is 1)
+ Offset uint
+}
+
// Param returns the named route parameter from the requests context.
-func Param(r *http.Request, name string) interface{} {
- return r.Context().Value(ctxParam(name))
+func Param(r *http.Request, name string) (pinfo ParamInfo, ok bool) {
+ v := r.Context().Value(ctxParam(name))
+ if v == nil {
+ return ParamInfo{}, false
+ }
+ return v.(ParamInfo), true
}
M params_test.go => params_test.go +36 -12
@@ 22,17 22,33 @@ func TestInvalidType(t *testing.T) {
var paramsTests = [...]struct {
routes []string
path string
- params map[string]interface{}
+ params map[string]mux.ParamInfo
noMatch bool
}{
0: {
routes: []string{"/user/{account uint}/{user int}/{name string}/{f float}"},
path: "/user/123/-11/me/1.123",
- params: map[string]interface{}{
- "account": uint64(123),
- "user": int64(-11),
- "name": "me",
- "f": float64(1.123),
+ params: map[string]mux.ParamInfo{
+ "account": mux.ParamInfo{
+ Value: uint64(123),
+ Raw: "123",
+ Offset: 6,
+ },
+ "user": mux.ParamInfo{
+ Value: int64(-11),
+ Raw: "-11",
+ Offset: 10,
+ },
+ "name": mux.ParamInfo{
+ Value: "me",
+ Raw: "me",
+ Offset: 14,
+ },
+ "f": mux.ParamInfo{
+ Value: float64(1.123),
+ Raw: "1.123",
+ Offset: 17,
+ },
},
},
1: {
@@ 43,8 59,12 @@ var paramsTests = [...]struct {
2: {
routes: []string{"/one/{other path}"},
path: "/one/two/three",
- params: map[string]interface{}{
- "other": "two/three",
+ params: map[string]mux.ParamInfo{
+ "other": mux.ParamInfo{
+ Value: "two/three",
+ Raw: "two/three",
+ Offset: 5,
+ },
},
},
3: {
@@ 66,13 86,17 @@ const (
notFoundStatusCode = 43
)
-func paramsHandler(t *testing.T, params map[string]interface{}) http.HandlerFunc {
+func paramsHandler(t *testing.T, params map[string]mux.ParamInfo) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(testStatusCode)
for k, v := range params {
- val := mux.Param(r, k)
- if !reflect.DeepEqual(val, v) {
- t.Errorf("Params has wrong type for %[1]q, want=%[2]T(%[2]v), got=%[3]T(%[3]v)", k, v, val)
+ pinfo, ok := mux.Param(r, k)
+ if !ok {
+ t.Errorf("No such parameter found %q", k)
+ continue
+ }
+ if !reflect.DeepEqual(pinfo, v) {
+ t.Errorf("Param do not match for %q, want=%+v, got=%+v)", k, v, pinfo)
}
}
}