mirror of https://github.com/gin-gonic/gin.git
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
725acf51eb
|
@ -21,7 +21,7 @@ jobs:
|
||||||
- name: Setup golangci-lint
|
- name: Setup golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v2
|
uses: golangci/golangci-lint-action@v2
|
||||||
with:
|
with:
|
||||||
version: v1.42.1
|
version: v1.43.0
|
||||||
args: --verbose
|
args: --verbose
|
||||||
test:
|
test:
|
||||||
needs: lint
|
needs: lint
|
||||||
|
|
12
context.go
12
context.go
|
@ -252,7 +252,7 @@ func (c *Context) Set(key string, value interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value for the given key, ie: (value, true).
|
// Get returns the value for the given key, ie: (value, true).
|
||||||
// If the value does not exists it returns (nil, false)
|
// If the value does not exist it returns (nil, false)
|
||||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
value, exists = c.Keys[key]
|
value, exists = c.Keys[key]
|
||||||
|
@ -602,7 +602,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind checks the Content-Type to select a binding engine automatically,
|
// Bind checks the Content-Type to select a binding engine automatically,
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
// Depending on the "Content-Type" header different bindings are used:
|
||||||
// "application/json" --> JSON binding
|
// "application/json" --> JSON binding
|
||||||
// "application/xml" --> XML binding
|
// "application/xml" --> XML binding
|
||||||
// otherwise --> returns an error.
|
// otherwise --> returns an error.
|
||||||
|
@ -661,7 +661,7 @@ func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBind checks the Content-Type to select a binding engine automatically,
|
// ShouldBind checks the Content-Type to select a binding engine automatically,
|
||||||
// Depending the "Content-Type" header different bindings are used:
|
// Depending on the "Content-Type" header different bindings are used:
|
||||||
// "application/json" --> JSON binding
|
// "application/json" --> JSON binding
|
||||||
// "application/xml" --> XML binding
|
// "application/xml" --> XML binding
|
||||||
// otherwise --> returns an error
|
// otherwise --> returns an error
|
||||||
|
@ -863,7 +863,7 @@ func (c *Context) Status(code int) {
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header is a intelligent shortcut for c.Writer.Header().Set(key, value).
|
// Header is an intelligent shortcut for c.Writer.Header().Set(key, value).
|
||||||
// It writes a header in the response.
|
// It writes a header in the response.
|
||||||
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
// If value == "", this method removes the header `c.Writer.Header().Del(key)`
|
||||||
func (c *Context) Header(key, value string) {
|
func (c *Context) Header(key, value string) {
|
||||||
|
@ -946,7 +946,7 @@ func (c *Context) HTML(code int, name string, obj interface{}) {
|
||||||
|
|
||||||
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
|
// IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body.
|
||||||
// It also sets the Content-Type as "application/json".
|
// It also sets the Content-Type as "application/json".
|
||||||
// WARNING: we recommend to use this only for development purposes since printing pretty JSON is
|
// WARNING: we recommend using this only for development purposes since printing pretty JSON is
|
||||||
// more CPU and bandwidth consuming. Use Context.JSON() instead.
|
// more CPU and bandwidth consuming. Use Context.JSON() instead.
|
||||||
func (c *Context) IndentedJSON(code int, obj interface{}) {
|
func (c *Context) IndentedJSON(code int, obj interface{}) {
|
||||||
c.Render(code, render.IndentedJSON{Data: obj})
|
c.Render(code, render.IndentedJSON{Data: obj})
|
||||||
|
@ -1010,7 +1010,7 @@ func (c *Context) String(code int, format string, values ...interface{}) {
|
||||||
c.Render(code, render.String{Format: format, Data: values})
|
c.Render(code, render.String{Format: format, Data: values})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect returns a HTTP redirect to the specific location.
|
// Redirect returns an HTTP redirect to the specific location.
|
||||||
func (c *Context) Redirect(code int, location string) {
|
func (c *Context) Redirect(code int, location string) {
|
||||||
c.Render(-1, render.Redirect{
|
c.Render(-1, render.Redirect{
|
||||||
Code: code,
|
Code: code,
|
||||||
|
|
|
@ -122,7 +122,7 @@ func (a errorMsgs) Last() *Error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errors returns an array will all the error messages.
|
// Errors returns an array with all the error messages.
|
||||||
// Example:
|
// Example:
|
||||||
// c.Error(errors.New("first"))
|
// c.Error(errors.New("first"))
|
||||||
// c.Error(errors.New("second"))
|
// c.Error(errors.New("second"))
|
||||||
|
|
46
tree.go
46
tree.go
|
@ -447,27 +447,26 @@ walk: // Outer loop for walking the tree
|
||||||
continue walk
|
continue walk
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
|
|
||||||
// the current node needs to roll back to last vaild skippedNode
|
|
||||||
|
|
||||||
if path != "/" && !n.wildChild {
|
if !n.wildChild {
|
||||||
for l := len(*skippedNodes); l > 0; {
|
// If the path at the end of the loop is not equal to '/' and the current node has no child nodes
|
||||||
skippedNode := (*skippedNodes)[l-1]
|
// the current node needs to roll back to last vaild skippedNode
|
||||||
*skippedNodes = (*skippedNodes)[:l-1]
|
if path != "/" {
|
||||||
if strings.HasSuffix(skippedNode.path, path) {
|
for l := len(*skippedNodes); l > 0; {
|
||||||
path = skippedNode.path
|
skippedNode := (*skippedNodes)[l-1]
|
||||||
n = skippedNode.node
|
*skippedNodes = (*skippedNodes)[:l-1]
|
||||||
if value.params != nil {
|
if strings.HasSuffix(skippedNode.path, path) {
|
||||||
*value.params = (*value.params)[:skippedNode.paramsCount]
|
path = skippedNode.path
|
||||||
|
n = skippedNode.node
|
||||||
|
if value.params != nil {
|
||||||
|
*value.params = (*value.params)[:skippedNode.paramsCount]
|
||||||
|
}
|
||||||
|
globalParamsCount = skippedNode.paramsCount
|
||||||
|
continue walk
|
||||||
}
|
}
|
||||||
globalParamsCount = skippedNode.paramsCount
|
|
||||||
continue walk
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// If there is no wildcard pattern, recommend a redirection
|
|
||||||
if !n.wildChild {
|
|
||||||
// Nothing found.
|
// Nothing found.
|
||||||
// We can recommend to redirect to the same URL without a
|
// We can recommend to redirect to the same URL without a
|
||||||
// trailing slash if a leaf exists for that path.
|
// trailing slash if a leaf exists for that path.
|
||||||
|
@ -614,8 +613,14 @@ walk: // Outer loop for walking the tree
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// roll back to last vaild skippedNode
|
// Nothing found. We can recommend to redirect to the same URL with an
|
||||||
if path != "/" {
|
// extra trailing slash if a leaf exists for that path
|
||||||
|
value.tsr = path == "/" ||
|
||||||
|
(len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
|
||||||
|
path == prefix[:len(prefix)-1] && n.handlers != nil)
|
||||||
|
|
||||||
|
// roll back to last valid skippedNode
|
||||||
|
if !value.tsr && path != "/" {
|
||||||
for l := len(*skippedNodes); l > 0; {
|
for l := len(*skippedNodes); l > 0; {
|
||||||
skippedNode := (*skippedNodes)[l-1]
|
skippedNode := (*skippedNodes)[l-1]
|
||||||
*skippedNodes = (*skippedNodes)[:l-1]
|
*skippedNodes = (*skippedNodes)[:l-1]
|
||||||
|
@ -631,11 +636,6 @@ walk: // Outer loop for walking the tree
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nothing found. We can recommend to redirect to the same URL with an
|
|
||||||
// extra trailing slash if a leaf exists for that path
|
|
||||||
value.tsr = path == "/" ||
|
|
||||||
(len(prefix) == len(path)+1 && prefix[len(path)] == '/' &&
|
|
||||||
path == prefix[:len(prefix)-1] && n.handlers != nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
27
tree_test.go
27
tree_test.go
|
@ -587,7 +587,14 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||||
"/doc/go1.html",
|
"/doc/go1.html",
|
||||||
"/no/a",
|
"/no/a",
|
||||||
"/no/b",
|
"/no/b",
|
||||||
"/api/hello/:name",
|
"/api/:page/:name",
|
||||||
|
"/api/hello/:name/bar/",
|
||||||
|
"/api/bar/:name",
|
||||||
|
"/api/baz/foo",
|
||||||
|
"/api/baz/foo/bar",
|
||||||
|
"/blog/:p",
|
||||||
|
"/posts/:b/:c",
|
||||||
|
"/posts/b/:c/d/",
|
||||||
}
|
}
|
||||||
for _, route := range routes {
|
for _, route := range routes {
|
||||||
recv := catchPanic(func() {
|
recv := catchPanic(func() {
|
||||||
|
@ -613,7 +620,19 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||||
"/admin/config/",
|
"/admin/config/",
|
||||||
"/admin/config/permissions/",
|
"/admin/config/permissions/",
|
||||||
"/doc/",
|
"/doc/",
|
||||||
|
"/admin/static/",
|
||||||
|
"/admin/cfg/",
|
||||||
|
"/admin/cfg/users/",
|
||||||
|
"/api/hello/x/bar",
|
||||||
|
"/api/baz/foo/",
|
||||||
|
"/api/baz/bax/",
|
||||||
|
"/api/bar/huh/",
|
||||||
|
"/api/baz/foo/bar/",
|
||||||
|
"/api/world/abc/",
|
||||||
|
"/blog/pp/",
|
||||||
|
"/posts/b/c/d",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, route := range tsrRoutes {
|
for _, route := range tsrRoutes {
|
||||||
value := tree.getValue(route, nil, getSkippedNodes(), false)
|
value := tree.getValue(route, nil, getSkippedNodes(), false)
|
||||||
if value.handlers != nil {
|
if value.handlers != nil {
|
||||||
|
@ -629,7 +648,11 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||||
"/no/",
|
"/no/",
|
||||||
"/_",
|
"/_",
|
||||||
"/_/",
|
"/_/",
|
||||||
"/api/world/abc",
|
"/api",
|
||||||
|
"/api/",
|
||||||
|
"/api/hello/x/foo",
|
||||||
|
"/api/baz/foo/bad",
|
||||||
|
"/foo/p/p",
|
||||||
}
|
}
|
||||||
for _, route := range noTsrRoutes {
|
for _, route := range noTsrRoutes {
|
||||||
value := tree.getValue(route, nil, getSkippedNodes(), false)
|
value := tree.getValue(route, nil, getSkippedNodes(), false)
|
||||||
|
|
|
@ -5,4 +5,4 @@
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
// Version is the current gin framework's version.
|
// Version is the current gin framework's version.
|
||||||
const Version = "v1.7.3"
|
const Version = "v1.7.4"
|
||||||
|
|
Loading…
Reference in New Issue