mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-23 01:57:55 +08:00
support bind uri (1)
This commit is contained in:
parent
c65e5efc9a
commit
e728e1c3dd
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
package binding
|
package binding
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
|
)
|
||||||
|
|
||||||
// Content-Type MIME of the most common data formats.
|
// Content-Type MIME of the most common data formats.
|
||||||
const (
|
const (
|
||||||
@ -35,6 +39,13 @@ type BindingBody interface {
|
|||||||
BindBody([]byte, interface{}) error
|
BindBody([]byte, interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||||
|
// but it read the Params.
|
||||||
|
type BindingUri interface {
|
||||||
|
Name() string
|
||||||
|
BindUri(internal.Params, interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
// StructValidator is the minimal interface which needs to be implemented in
|
// StructValidator is the minimal interface which needs to be implemented in
|
||||||
// order for it to be used as the validator engine for ensuring the correctness
|
// order for it to be used as the validator engine for ensuring the correctness
|
||||||
// of the reqest. Gin provides a default implementation for this using
|
// of the reqest. Gin provides a default implementation for this using
|
||||||
@ -68,6 +79,7 @@ var (
|
|||||||
FormMultipart = formMultipartBinding{}
|
FormMultipart = formMultipartBinding{}
|
||||||
ProtoBuf = protobufBinding{}
|
ProtoBuf = protobufBinding{}
|
||||||
MsgPack = msgpackBinding{}
|
MsgPack = msgpackBinding{}
|
||||||
|
Uri = uriBinding{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default returns the appropriate Binding instance based on the HTTP method
|
// Default returns the appropriate Binding instance based on the HTTP method
|
||||||
|
@ -10,8 +10,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func mapUri(ptr interface{}, ps internal.Params) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func mapForm(ptr interface{}, form map[string][]string) error {
|
func mapForm(ptr interface{}, form map[string][]string) error {
|
||||||
typ := reflect.TypeOf(ptr).Elem()
|
typ := reflect.TypeOf(ptr).Elem()
|
||||||
val := reflect.ValueOf(ptr).Elem()
|
val := reflect.ValueOf(ptr).Elem()
|
||||||
|
20
binding/uri.go
Normal file
20
binding/uri.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package binding
|
||||||
|
|
||||||
|
import "github.com/gin-gonic/gin/internal"
|
||||||
|
|
||||||
|
type uriBinding struct{}
|
||||||
|
|
||||||
|
func (uriBinding) Name() string {
|
||||||
|
return "uri"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uriBinding) BindUri(p internal.Params, obj interface{}) error {
|
||||||
|
if err := mapUri(obj, p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
12
context.go
12
context.go
@ -19,6 +19,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-contrib/sse"
|
"github.com/gin-contrib/sse"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
"github.com/gin-gonic/gin/render"
|
"github.com/gin-gonic/gin/render"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ type Context struct {
|
|||||||
Request *http.Request
|
Request *http.Request
|
||||||
Writer ResponseWriter
|
Writer ResponseWriter
|
||||||
|
|
||||||
Params Params
|
Params internal.Params
|
||||||
handlers HandlersChain
|
handlers HandlersChain
|
||||||
index int8
|
index int8
|
||||||
|
|
||||||
@ -563,6 +564,11 @@ func (c *Context) ShouldBindQuery(obj interface{}) error {
|
|||||||
return c.ShouldBindWith(obj, binding.Query)
|
return c.ShouldBindWith(obj, binding.Query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||||
|
func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||||
|
return binding.Uri.BindUri(c.Params, obj)
|
||||||
|
}
|
||||||
|
|
||||||
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
||||||
@ -574,9 +580,7 @@ func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error {
|
|||||||
//
|
//
|
||||||
// NOTE: This method reads the body before binding. So you should use
|
// NOTE: This method reads the body before binding. So you should use
|
||||||
// ShouldBindWith for better performance if you need to call only once.
|
// ShouldBindWith for better performance if you need to call only once.
|
||||||
func (c *Context) ShouldBindBodyWith(
|
func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) {
|
||||||
obj interface{}, bb binding.BindingBody,
|
|
||||||
) (err error) {
|
|
||||||
var body []byte
|
var body []byte
|
||||||
if cb, ok := c.Get(BodyBytesKey); ok {
|
if cb, ok := c.Get(BodyBytesKey); ok {
|
||||||
if cbb, ok := cb.([]byte); ok {
|
if cbb, ok := cb.([]byte); ok {
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-contrib/sse"
|
"github.com/gin-contrib/sse"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
@ -158,7 +159,7 @@ func TestContextReset(t *testing.T) {
|
|||||||
|
|
||||||
c.index = 2
|
c.index = 2
|
||||||
c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()}
|
c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()}
|
||||||
c.Params = Params{Param{}}
|
c.Params = internal.Params{internal.Param{}}
|
||||||
c.Error(errors.New("test"))
|
c.Error(errors.New("test"))
|
||||||
c.Set("foo", "bar")
|
c.Set("foo", "bar")
|
||||||
c.reset()
|
c.reset()
|
||||||
@ -316,7 +317,7 @@ func TestContextCopy(t *testing.T) {
|
|||||||
c.index = 2
|
c.index = 2
|
||||||
c.Request, _ = http.NewRequest("POST", "/hola", nil)
|
c.Request, _ = http.NewRequest("POST", "/hola", nil)
|
||||||
c.handlers = HandlersChain{func(c *Context) {}}
|
c.handlers = HandlersChain{func(c *Context) {}}
|
||||||
c.Params = Params{Param{Key: "foo", Value: "bar"}}
|
c.Params = internal.Params{internal.Param{Key: "foo", Value: "bar"}}
|
||||||
c.Set("foo", "bar")
|
c.Set("foo", "bar")
|
||||||
|
|
||||||
cp := c.Copy()
|
cp := c.Copy()
|
||||||
|
@ -14,6 +14,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
type route struct {
|
type route struct {
|
||||||
@ -316,9 +318,9 @@ func TestGithubAPI(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func exampleFromPath(path string) (string, Params) {
|
func exampleFromPath(path string) (string, internal.Params) {
|
||||||
output := new(bytes.Buffer)
|
output := new(bytes.Buffer)
|
||||||
params := make(Params, 0, 6)
|
params := make(internal.Params, 0, 6)
|
||||||
start := -1
|
start := -1
|
||||||
for i, c := range path {
|
for i, c := range path {
|
||||||
if c == ':' {
|
if c == ':' {
|
||||||
@ -327,7 +329,7 @@ func exampleFromPath(path string) (string, Params) {
|
|||||||
if start >= 0 {
|
if start >= 0 {
|
||||||
if c == '/' {
|
if c == '/' {
|
||||||
value := fmt.Sprint(rand.Intn(100000))
|
value := fmt.Sprint(rand.Intn(100000))
|
||||||
params = append(params, Param{
|
params = append(params, internal.Param{
|
||||||
Key: path[start:i],
|
Key: path[start:i],
|
||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
@ -341,7 +343,7 @@ func exampleFromPath(path string) (string, Params) {
|
|||||||
}
|
}
|
||||||
if start >= 0 {
|
if start >= 0 {
|
||||||
value := fmt.Sprint(rand.Intn(100000))
|
value := fmt.Sprint(rand.Intn(100000))
|
||||||
params = append(params, Param{
|
params = append(params, internal.Param{
|
||||||
Key: path[start:],
|
Key: path[start:],
|
||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
|
34
internal/internal.go
Normal file
34
internal/internal.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2018 Gin Core Team. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package internal
|
||||||
|
|
||||||
|
// Param is a single URL parameter, consisting of a key and a value.
|
||||||
|
type Param struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Params is a Param-slice, as returned by the router.
|
||||||
|
// The slice is ordered, the first URL parameter is also the first slice value.
|
||||||
|
// It is therefore safe to read values by the index.
|
||||||
|
type Params []Param
|
||||||
|
|
||||||
|
// Get returns the value of the first Param which key matches the given name.
|
||||||
|
// If no matching Param is found, an empty string is returned.
|
||||||
|
func (ps Params) Get(name string) (string, bool) {
|
||||||
|
for _, entry := range ps {
|
||||||
|
if entry.Key == name {
|
||||||
|
return entry.Value, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ByName returns the value of the first Param which key matches the given name.
|
||||||
|
// If no matching Param is found, an empty string is returned.
|
||||||
|
func (ps Params) ByName(name string) (va string) {
|
||||||
|
va, _ = ps.Get(name)
|
||||||
|
return
|
||||||
|
}
|
37
tree.go
37
tree.go
@ -8,37 +8,10 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Param is a single URL parameter, consisting of a key and a value.
|
|
||||||
type Param struct {
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Params is a Param-slice, as returned by the router.
|
|
||||||
// The slice is ordered, the first URL parameter is also the first slice value.
|
|
||||||
// It is therefore safe to read values by the index.
|
|
||||||
type Params []Param
|
|
||||||
|
|
||||||
// Get returns the value of the first Param which key matches the given name.
|
|
||||||
// If no matching Param is found, an empty string is returned.
|
|
||||||
func (ps Params) Get(name string) (string, bool) {
|
|
||||||
for _, entry := range ps {
|
|
||||||
if entry.Key == name {
|
|
||||||
return entry.Value, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// ByName returns the value of the first Param which key matches the given name.
|
|
||||||
// If no matching Param is found, an empty string is returned.
|
|
||||||
func (ps Params) ByName(name string) (va string) {
|
|
||||||
va, _ = ps.Get(name)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type methodTree struct {
|
type methodTree struct {
|
||||||
method string
|
method string
|
||||||
root *node
|
root *node
|
||||||
@ -369,7 +342,7 @@ func (n *node) insertChild(numParams uint8, path string, fullPath string, handle
|
|||||||
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
|
||||||
// made if a handle exists with an extra (without the) trailing slash for the
|
// made if a handle exists with an extra (without the) trailing slash for the
|
||||||
// given path.
|
// given path.
|
||||||
func (n *node) getValue(path string, po Params, unescape bool) (handlers HandlersChain, p Params, tsr bool) {
|
func (n *node) getValue(path string, po internal.Params, unescape bool) (handlers HandlersChain, p internal.Params, tsr bool) {
|
||||||
p = po
|
p = po
|
||||||
walk: // Outer loop for walking the tree
|
walk: // Outer loop for walking the tree
|
||||||
for {
|
for {
|
||||||
@ -407,7 +380,7 @@ walk: // Outer loop for walking the tree
|
|||||||
|
|
||||||
// save param value
|
// save param value
|
||||||
if cap(p) < int(n.maxParams) {
|
if cap(p) < int(n.maxParams) {
|
||||||
p = make(Params, 0, n.maxParams)
|
p = make(internal.Params, 0, n.maxParams)
|
||||||
}
|
}
|
||||||
i := len(p)
|
i := len(p)
|
||||||
p = p[:i+1] // expand slice within preallocated capacity
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
@ -450,7 +423,7 @@ walk: // Outer loop for walking the tree
|
|||||||
case catchAll:
|
case catchAll:
|
||||||
// save param value
|
// save param value
|
||||||
if cap(p) < int(n.maxParams) {
|
if cap(p) < int(n.maxParams) {
|
||||||
p = make(Params, 0, n.maxParams)
|
p = make(internal.Params, 0, n.maxParams)
|
||||||
}
|
}
|
||||||
i := len(p)
|
i := len(p)
|
||||||
p = p[:i+1] // expand slice within preallocated capacity
|
p = p[:i+1] // expand slice within preallocated capacity
|
||||||
|
58
tree_test.go
58
tree_test.go
@ -10,6 +10,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Used as a workaround since we can't compare functions or their addressses
|
// Used as a workaround since we can't compare functions or their addressses
|
||||||
@ -25,7 +27,7 @@ type testRequests []struct {
|
|||||||
path string
|
path string
|
||||||
nilHandler bool
|
nilHandler bool
|
||||||
route string
|
route string
|
||||||
ps Params
|
ps internal.Params
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) {
|
func checkRequests(t *testing.T, tree *node, requests testRequests, unescapes ...bool) {
|
||||||
@ -170,19 +172,19 @@ func TestTreeWildcard(t *testing.T) {
|
|||||||
|
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
{"/cmd/test/", false, "/cmd/:tool/", internal.Params{internal.Param{"tool", "test"}}},
|
||||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
|
{"/cmd/test", true, "", internal.Params{internal.Param{"tool", "test"}}},
|
||||||
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}},
|
{"/cmd/test/3", false, "/cmd/:tool/:sub", internal.Params{internal.Param{"tool", "test"}, internal.Param{"sub", "3"}}},
|
||||||
{"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}},
|
{"/src/", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/"}}},
|
||||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
{"/src/some/file.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file.png"}}},
|
||||||
{"/search/", false, "/search/", nil},
|
{"/search/", false, "/search/", nil},
|
||||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", internal.Params{internal.Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
{"/search/someth!ng+in+ünìcodé/", true, "", internal.Params{internal.Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
{"/user_gopher", false, "/user_:name", internal.Params{internal.Param{"name", "gopher"}}},
|
||||||
{"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}},
|
{"/user_gopher/about", false, "/user_:name/about", internal.Params{internal.Param{"name", "gopher"}}},
|
||||||
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}},
|
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", internal.Params{internal.Param{"dir", "js"}, internal.Param{"filepath", "/inc/framework.js"}}},
|
||||||
{"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}},
|
{"/info/gordon/public", false, "/info/:user/public", internal.Params{internal.Param{"user", "gordon"}}},
|
||||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
|
{"/info/gordon/project/go", false, "/info/:user/project/:project", internal.Params{internal.Param{"user", "gordon"}, internal.Param{"project", "go"}}},
|
||||||
})
|
})
|
||||||
|
|
||||||
checkPriorities(t, tree)
|
checkPriorities(t, tree)
|
||||||
@ -209,18 +211,18 @@ func TestUnescapeParameters(t *testing.T) {
|
|||||||
unescape := true
|
unescape := true
|
||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
{"/cmd/test/", false, "/cmd/:tool/", internal.Params{internal.Param{"tool", "test"}}},
|
||||||
{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
|
{"/cmd/test", true, "", internal.Params{internal.Param{"tool", "test"}}},
|
||||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
{"/src/some/file.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file.png"}}},
|
||||||
{"/src/some/file+test.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file test.png"}}},
|
{"/src/some/file+test.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file test.png"}}},
|
||||||
{"/src/some/file++++%%%%test.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file++++%%%%test.png"}}},
|
{"/src/some/file++++%%%%test.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file++++%%%%test.png"}}},
|
||||||
{"/src/some/file%2Ftest.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file/test.png"}}},
|
{"/src/some/file%2Ftest.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file/test.png"}}},
|
||||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng in ünìcodé"}}},
|
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", internal.Params{internal.Param{"query", "someth!ng in ünìcodé"}}},
|
||||||
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
|
{"/info/gordon/project/go", false, "/info/:user/project/:project", internal.Params{internal.Param{"user", "gordon"}, internal.Param{"project", "go"}}},
|
||||||
{"/info/slash%2Fgordon", false, "/info/:user", Params{Param{"user", "slash/gordon"}}},
|
{"/info/slash%2Fgordon", false, "/info/:user", internal.Params{internal.Param{"user", "slash/gordon"}}},
|
||||||
{"/info/slash%2Fgordon/project/Project%20%231", false, "/info/:user/project/:project", Params{Param{"user", "slash/gordon"}, Param{"project", "Project #1"}}},
|
{"/info/slash%2Fgordon/project/Project%20%231", false, "/info/:user/project/:project", internal.Params{internal.Param{"user", "slash/gordon"}, internal.Param{"project", "Project #1"}}},
|
||||||
{"/info/slash%%%%", false, "/info/:user", Params{Param{"user", "slash%%%%"}}},
|
{"/info/slash%%%%", false, "/info/:user", internal.Params{internal.Param{"user", "slash%%%%"}}},
|
||||||
{"/info/slash%%%%2Fgordon/project/Project%%%%20%231", false, "/info/:user/project/:project", Params{Param{"user", "slash%%%%2Fgordon"}, Param{"project", "Project%%%%20%231"}}},
|
{"/info/slash%%%%2Fgordon/project/Project%%%%20%231", false, "/info/:user/project/:project", internal.Params{internal.Param{"user", "slash%%%%2Fgordon"}, internal.Param{"project", "Project%%%%20%231"}}},
|
||||||
}, unescape)
|
}, unescape)
|
||||||
|
|
||||||
checkPriorities(t, tree)
|
checkPriorities(t, tree)
|
||||||
@ -326,9 +328,9 @@ func TestTreeDupliatePath(t *testing.T) {
|
|||||||
checkRequests(t, tree, testRequests{
|
checkRequests(t, tree, testRequests{
|
||||||
{"/", false, "/", nil},
|
{"/", false, "/", nil},
|
||||||
{"/doc/", false, "/doc/", nil},
|
{"/doc/", false, "/doc/", nil},
|
||||||
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
{"/src/some/file.png", false, "/src/*filepath", internal.Params{internal.Param{"filepath", "/some/file.png"}}},
|
||||||
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", internal.Params{internal.Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
{"/user_gopher", false, "/user_:name", internal.Params{internal.Param{"name", "gopher"}}},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user