mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-24 02:32:17 +08:00
Merge branch 'master' into make
This commit is contained in:
commit
a3cca26cfd
12
README.md
12
README.md
@ -117,7 +117,7 @@ $ go get github.com/kardianos/govendor
|
|||||||
2. Create your project folder and `cd` inside
|
2. Create your project folder and `cd` inside
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ mkdir -p ~/go/src/github.com/myusername/project && cd "$_"
|
$ mkdir -p $GOPATH/src/github.com/myusername/project && cd "$_"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Vendor init your project and add gin
|
3. Vendor init your project and add gin
|
||||||
@ -385,7 +385,7 @@ func main() {
|
|||||||
r := gin.New()
|
r := gin.New()
|
||||||
|
|
||||||
// Global middleware
|
// Global middleware
|
||||||
// Logger middleware will write the logs to gin.DefaultWriter even you set with GIN_MODE=release.
|
// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
|
||||||
// By default gin.DefaultWriter = os.Stdout
|
// By default gin.DefaultWriter = os.Stdout
|
||||||
r.Use(gin.Logger())
|
r.Use(gin.Logger())
|
||||||
|
|
||||||
@ -435,7 +435,7 @@ func main() {
|
|||||||
c.String(200, "pong")
|
c.String(200, "pong")
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -540,7 +540,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
validator "gopkg.in/go-playground/validator.v8"
|
"gopkg.in/go-playground/validator.v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Booking struct {
|
type Booking struct {
|
||||||
@ -1071,7 +1071,7 @@ func main() {
|
|||||||
|
|
||||||
### Goroutines inside a middleware
|
### Goroutines inside a middleware
|
||||||
|
|
||||||
When starting inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
@ -1190,7 +1190,7 @@ func main() {
|
|||||||
|
|
||||||
### Run multiple service using Gin
|
### Run multiple service using Gin
|
||||||
|
|
||||||
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the folling example:
|
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
|
||||||
|
|
||||||
[embedmd]:# (examples/multiple-service/main.go go)
|
[embedmd]:# (examples/multiple-service/main.go go)
|
||||||
```go
|
```go
|
||||||
|
12
auth.go
12
auth.go
@ -17,8 +17,8 @@ const AuthUserKey = "user"
|
|||||||
type Accounts map[string]string
|
type Accounts map[string]string
|
||||||
|
|
||||||
type authPair struct {
|
type authPair struct {
|
||||||
Value string
|
value string
|
||||||
User string
|
user string
|
||||||
}
|
}
|
||||||
|
|
||||||
type authPairs []authPair
|
type authPairs []authPair
|
||||||
@ -28,8 +28,8 @@ func (a authPairs) searchCredential(authValue string) (string, bool) {
|
|||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
for _, pair := range a {
|
for _, pair := range a {
|
||||||
if pair.Value == authValue {
|
if pair.value == authValue {
|
||||||
return pair.User, true
|
return pair.user, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
@ -74,8 +74,8 @@ func processAccounts(accounts Accounts) authPairs {
|
|||||||
assert1(user != "", "User can not be empty")
|
assert1(user != "", "User can not be empty")
|
||||||
value := authorizationHeader(user, password)
|
value := authorizationHeader(user, password)
|
||||||
pairs = append(pairs, authPair{
|
pairs = append(pairs, authPair{
|
||||||
Value: value,
|
value: value,
|
||||||
User: user,
|
user: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return pairs
|
return pairs
|
||||||
|
12
auth_test.go
12
auth_test.go
@ -22,16 +22,16 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
|
|
||||||
assert.Len(t, pairs, 3)
|
assert.Len(t, pairs, 3)
|
||||||
assert.Contains(t, pairs, authPair{
|
assert.Contains(t, pairs, authPair{
|
||||||
User: "bar",
|
user: "bar",
|
||||||
Value: "Basic YmFyOmZvbw==",
|
value: "Basic YmFyOmZvbw==",
|
||||||
})
|
})
|
||||||
assert.Contains(t, pairs, authPair{
|
assert.Contains(t, pairs, authPair{
|
||||||
User: "foo",
|
user: "foo",
|
||||||
Value: "Basic Zm9vOmJhcg==",
|
value: "Basic Zm9vOmJhcg==",
|
||||||
})
|
})
|
||||||
assert.Contains(t, pairs, authPair{
|
assert.Contains(t, pairs, authPair{
|
||||||
User: "admin",
|
user: "admin",
|
||||||
Value: "Basic YWRtaW46cGFzc3dvcmQ=",
|
value: "Basic YWRtaW46cGFzc3dvcmQ=",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +59,6 @@ func BenchmarkOneRouteJSON(B *testing.B) {
|
|||||||
runRequest(B, router, "GET", "/json")
|
runRequest(B, router, "GET", "/json")
|
||||||
}
|
}
|
||||||
|
|
||||||
var htmlContentType = []string{"text/html; charset=utf-8"}
|
|
||||||
|
|
||||||
func BenchmarkOneRouteHTML(B *testing.B) {
|
func BenchmarkOneRouteHTML(B *testing.B) {
|
||||||
router := New()
|
router := New()
|
||||||
t := template.Must(template.New("index").Parse(`
|
t := template.Must(template.New("index").Parse(`
|
||||||
|
@ -179,12 +179,3 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
|
|||||||
value.Set(reflect.ValueOf(t))
|
value.Set(reflect.ValueOf(t))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't pass in pointers to bind to. Can lead to bugs. See:
|
|
||||||
// https://github.com/codegangsta/martini-contrib/issues/40
|
|
||||||
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
|
|
||||||
func ensureNotPointer(obj interface{}) {
|
|
||||||
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
|
|
||||||
panic("Pointers are not accepted as binding models")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -449,10 +449,10 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
|
|||||||
// Depending the "Content-Type" header different bindings are used:
|
// Depending 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.
|
||||||
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
// It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input.
|
||||||
// It decodes the json payload into the struct specified as a pointer.
|
// It decodes the json payload into the struct specified as a pointer.
|
||||||
// It will writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
|
// It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid.
|
||||||
func (c *Context) Bind(obj interface{}) error {
|
func (c *Context) Bind(obj interface{}) error {
|
||||||
b := binding.Default(c.Request.Method, c.ContentType())
|
b := binding.Default(c.Request.Method, c.ContentType())
|
||||||
return c.MustBindWith(obj, b)
|
return c.MustBindWith(obj, b)
|
||||||
|
@ -676,6 +676,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
|||||||
func TestContextRenderHTML(t *testing.T) {
|
func TestContextRenderHTML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, router := CreateTestContext(w)
|
c, router := CreateTestContext(w)
|
||||||
|
|
||||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
@ -686,6 +687,30 @@ func TestContextRenderHTML(t *testing.T) {
|
|||||||
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextRenderHTML2(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, router := CreateTestContext(w)
|
||||||
|
|
||||||
|
// print debug warning log when Engine.trees > 0
|
||||||
|
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
||||||
|
assert.Len(t, router.trees, 1)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
setup(&b)
|
||||||
|
defer teardown()
|
||||||
|
|
||||||
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
|
assert.Equal(t, "[GIN-debug] [WARNING] Since SetHTMLTemplate() is NOT thread-safe. It should only be called\nat initialization. ie. before any route is registered or the router is listening in a socket:\n\n\trouter := gin.Default()\n\trouter.SetHTMLTemplate(template) // << good place\n\n", b.String())
|
||||||
|
|
||||||
|
c.HTML(201, "t", H{"name": "alexandernyquist"})
|
||||||
|
|
||||||
|
assert.Equal(t, 201, w.Code)
|
||||||
|
assert.Equal(t, "Hello alexandernyquist", w.Body.String())
|
||||||
|
assert.Equal(t, "text/html; charset=utf-8", w.HeaderMap.Get("Content-Type"))
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that no HTML is rendered if code is 204
|
// Tests that no HTML is rendered if code is 204
|
||||||
func TestContextRenderNoContentHTML(t *testing.T) {
|
func TestContextRenderNoContentHTML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
|
31
deprecated_test.go
Normal file
31
deprecated_test.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBindWith(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/?foo=bar&bar=foo", bytes.NewBufferString("foo=unused"))
|
||||||
|
|
||||||
|
var obj struct {
|
||||||
|
Foo string `form:"foo"`
|
||||||
|
Bar string `form:"bar"`
|
||||||
|
}
|
||||||
|
assert.NoError(t, c.BindWith(&obj, binding.Form))
|
||||||
|
assert.Equal(t, "foo", obj.Bar)
|
||||||
|
assert.Equal(t, "bar", obj.Foo)
|
||||||
|
assert.Equal(t, 0, w.Body.Len())
|
||||||
|
}
|
@ -148,7 +148,7 @@ func (a errorMsgs) String() string {
|
|||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for i, msg := range a {
|
for i, msg := range a {
|
||||||
fmt.Fprintf(&buffer, "Error #%02d: %s\n", (i + 1), msg.Err)
|
fmt.Fprintf(&buffer, "Error #%02d: %s\n", i+1, msg.Err)
|
||||||
if msg.Meta != nil {
|
if msg.Meta != nil {
|
||||||
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
|
fmt.Fprintf(&buffer, " Meta: %v\n", msg.Meta)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
validator "gopkg.in/go-playground/validator.v8"
|
"gopkg.in/go-playground/validator.v8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Booking struct {
|
type Booking struct {
|
||||||
|
@ -29,8 +29,8 @@ func statsWorker() {
|
|||||||
"timestamp": uint64(time.Now().Unix()),
|
"timestamp": uint64(time.Now().Unix()),
|
||||||
"HeapInuse": stats.HeapInuse,
|
"HeapInuse": stats.HeapInuse,
|
||||||
"StackInuse": stats.StackInuse,
|
"StackInuse": stats.StackInuse,
|
||||||
"Mallocs": (stats.Mallocs - lastMallocs),
|
"Mallocs": stats.Mallocs - lastMallocs,
|
||||||
"Frees": (stats.Frees - lastFrees),
|
"Frees": stats.Frees - lastFrees,
|
||||||
"Inbound": uint64(messages.Get("inbound")),
|
"Inbound": uint64(messages.Get("inbound")),
|
||||||
"Outbound": uint64(messages.Get("outbound")),
|
"Outbound": uint64(messages.Get("outbound")),
|
||||||
"Connected": connectedUsers(),
|
"Connected": connectedUsers(),
|
||||||
|
28
gin.go
28
gin.go
@ -49,16 +49,6 @@ type RoutesInfo []RouteInfo
|
|||||||
// Create an instance of Engine, by using New() or Default()
|
// Create an instance of Engine, by using New() or Default()
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
RouterGroup
|
RouterGroup
|
||||||
delims render.Delims
|
|
||||||
secureJsonPrefix string
|
|
||||||
HTMLRender render.HTMLRender
|
|
||||||
FuncMap template.FuncMap
|
|
||||||
allNoRoute HandlersChain
|
|
||||||
allNoMethod HandlersChain
|
|
||||||
noRoute HandlersChain
|
|
||||||
noMethod HandlersChain
|
|
||||||
pool sync.Pool
|
|
||||||
trees methodTrees
|
|
||||||
|
|
||||||
// Enables automatic redirection if the current route can't be matched but a
|
// Enables automatic redirection if the current route can't be matched but a
|
||||||
// handler for the path with (without) the trailing slash exists.
|
// handler for the path with (without) the trailing slash exists.
|
||||||
@ -102,6 +92,17 @@ type Engine struct {
|
|||||||
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
|
// Value of 'maxMemory' param that is given to http.Request's ParseMultipartForm
|
||||||
// method call.
|
// method call.
|
||||||
MaxMultipartMemory int64
|
MaxMultipartMemory int64
|
||||||
|
|
||||||
|
delims render.Delims
|
||||||
|
secureJsonPrefix string
|
||||||
|
HTMLRender render.HTMLRender
|
||||||
|
FuncMap template.FuncMap
|
||||||
|
allNoRoute HandlersChain
|
||||||
|
allNoMethod HandlersChain
|
||||||
|
noRoute HandlersChain
|
||||||
|
noMethod HandlersChain
|
||||||
|
pool sync.Pool
|
||||||
|
trees methodTrees
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ IRouter = &Engine{}
|
var _ IRouter = &Engine{}
|
||||||
@ -159,11 +160,14 @@ func (engine *Engine) Delims(left, right string) *Engine {
|
|||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecureJsonPrefix sets the secureJsonPrefix used in Context.SecureJSON.
|
||||||
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
|
func (engine *Engine) SecureJsonPrefix(prefix string) *Engine {
|
||||||
engine.secureJsonPrefix = prefix
|
engine.secureJsonPrefix = prefix
|
||||||
return engine
|
return engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadHTMLGlob loads HTML files identified by glob pattern
|
||||||
|
// and associates the result with HTML renderer.
|
||||||
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||||
left := engine.delims.Left
|
left := engine.delims.Left
|
||||||
right := engine.delims.Right
|
right := engine.delims.Right
|
||||||
@ -178,6 +182,8 @@ func (engine *Engine) LoadHTMLGlob(pattern string) {
|
|||||||
engine.SetHTMLTemplate(templ)
|
engine.SetHTMLTemplate(templ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadHTMLFiles loads a slice of HTML files
|
||||||
|
// and associates the result with HTML renderer.
|
||||||
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||||
if IsDebugging() {
|
if IsDebugging() {
|
||||||
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
|
engine.HTMLRender = render.HTMLDebug{Files: files, FuncMap: engine.FuncMap, Delims: engine.delims}
|
||||||
@ -188,6 +194,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) {
|
|||||||
engine.SetHTMLTemplate(templ)
|
engine.SetHTMLTemplate(templ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetHTMLTemplate associate a template with HTML renderer.
|
||||||
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
||||||
if len(engine.trees) > 0 {
|
if len(engine.trees) > 0 {
|
||||||
debugPrintWARNINGSetHTMLTemplate()
|
debugPrintWARNINGSetHTMLTemplate()
|
||||||
@ -196,6 +203,7 @@ func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
|
|||||||
engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
|
engine.HTMLRender = render.HTMLProduction{Template: templ.Funcs(engine.FuncMap)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFuncMap sets the FuncMap used for template.FuncMap.
|
||||||
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
|
func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
|
||||||
engine.FuncMap = funcMap
|
engine.FuncMap = funcMap
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func TestUnixSocket(t *testing.T) {
|
|||||||
c, err := net.Dial("unix", "/tmp/unix_unit_test")
|
c, err := net.Dial("unix", "/tmp/unix_unit_test")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
fmt.Fprintf(c, "GET /example HTTP/1.0\r\n\r\n")
|
fmt.Fprint(c, "GET /example HTTP/1.0\r\n\r\n")
|
||||||
scanner := bufio.NewScanner(c)
|
scanner := bufio.NewScanner(c)
|
||||||
var response string
|
var response string
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
|
6
mode.go
6
mode.go
@ -14,9 +14,9 @@ import (
|
|||||||
const ENV_GIN_MODE = "GIN_MODE"
|
const ENV_GIN_MODE = "GIN_MODE"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DebugMode string = "debug"
|
DebugMode = "debug"
|
||||||
ReleaseMode string = "release"
|
ReleaseMode = "release"
|
||||||
TestMode string = "test"
|
TestMode = "test"
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
debugCode = iota
|
debugCode = iota
|
||||||
|
@ -111,10 +111,8 @@ func (h xmlmap) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
|
|
||||||
return err
|
return e.EncodeToken(xml.EndElement{Name: start.Name})
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRenderXML(t *testing.T) {
|
func TestRenderXML(t *testing.T) {
|
||||||
|
14
tree.go
14
tree.go
@ -87,13 +87,13 @@ const (
|
|||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
path string
|
path string
|
||||||
wildChild bool
|
|
||||||
nType nodeType
|
|
||||||
maxParams uint8
|
|
||||||
indices string
|
indices string
|
||||||
children []*node
|
children []*node
|
||||||
handlers HandlersChain
|
handlers HandlersChain
|
||||||
priority uint32
|
priority uint32
|
||||||
|
nType nodeType
|
||||||
|
maxParams uint8
|
||||||
|
wildChild bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// increments priority of the given child and reorders if necessary.
|
// increments priority of the given child and reorders if necessary.
|
||||||
@ -384,7 +384,7 @@ walk: // Outer loop for walking the tree
|
|||||||
// 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.
|
||||||
tsr = (path == "/" && n.handlers != nil)
|
tsr = path == "/" && n.handlers != nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +424,7 @@ walk: // Outer loop for walking the tree
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... but we can't
|
// ... but we can't
|
||||||
tsr = (len(path) == end+1)
|
tsr = len(path) == end+1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,7 +435,7 @@ walk: // Outer loop for walking the tree
|
|||||||
// No handle found. Check if a handle for this path + a
|
// No handle found. Check if a handle for this path + a
|
||||||
// trailing slash exists for TSR recommendation
|
// trailing slash exists for TSR recommendation
|
||||||
n = n.children[0]
|
n = n.children[0]
|
||||||
tsr = (n.path == "/" && n.handlers != nil)
|
tsr = n.path == "/" && n.handlers != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -530,7 +530,7 @@ func (n *node) findCaseInsensitivePath(path string, fixTrailingSlash bool) (ciPa
|
|||||||
|
|
||||||
// Nothing found. We can recommend to redirect to the same URL
|
// Nothing found. We can recommend to redirect to the same URL
|
||||||
// without a trailing slash if a leaf exists for that path
|
// without a trailing slash if a leaf exists for that path
|
||||||
found = (fixTrailingSlash && path == "/" && n.handlers != nil)
|
found = fixTrailingSlash && path == "/" && n.handlers != nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11
tree_test.go
11
tree_test.go
@ -5,22 +5,11 @@
|
|||||||
package gin
|
package gin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printChildren(n *node, prefix string) {
|
|
||||||
fmt.Printf(" %02d:%02d %s%s[%d] %v %t %d \r\n", n.priority, n.maxParams, prefix, n.path, len(n.children), n.handlers, n.wildChild, n.nType)
|
|
||||||
for l := len(n.path); l > 0; l-- {
|
|
||||||
prefix += " "
|
|
||||||
}
|
|
||||||
for _, child := range n.children {
|
|
||||||
printChildren(child, prefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
var fakeHandlerValue string
|
var fakeHandlerValue string
|
||||||
|
|
||||||
|
11
utils.go
11
utils.go
@ -33,18 +33,23 @@ func Bind(val interface{}) HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WrapF is a helper function for wrapping http.HandlerFunc
|
||||||
|
// Returns a Gin middleware
|
||||||
func WrapF(f http.HandlerFunc) HandlerFunc {
|
func WrapF(f http.HandlerFunc) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
f(c.Writer, c.Request)
|
f(c.Writer, c.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WrapH is a helper function for wrapping http.Handler
|
||||||
|
// Returns a Gin middleware
|
||||||
func WrapH(h http.Handler) HandlerFunc {
|
func WrapH(h http.Handler) HandlerFunc {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
h.ServeHTTP(c.Writer, c.Request)
|
h.ServeHTTP(c.Writer, c.Request)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// H is a shortcup for map[string]interface{}
|
||||||
type H map[string]interface{}
|
type H map[string]interface{}
|
||||||
|
|
||||||
// MarshalXML allows type H to be used with xml.Marshal.
|
// MarshalXML allows type H to be used with xml.Marshal.
|
||||||
@ -65,10 +70,8 @@ func (h H) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
|
|
||||||
return err
|
return e.EncodeToken(xml.EndElement{Name: start.Name})
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func assert1(guard bool, text string) {
|
func assert1(guard bool, text string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user