mirror of https://github.com/gin-gonic/gin.git
refactored Engine.handleHTTPRequest so that two passes are made if the request is HEAD and there isn't a matching HEAD handler
added more documentation re-ordered GET and HEAD methods to be adjacent rendering is now suppressed for HEAD request, which means that wasteful processing is avoided
This commit is contained in:
parent
65ed60ed13
commit
652faa23c2
16
README.md
16
README.md
|
@ -196,6 +196,8 @@ You can find a number of ready-to-run examples at [Gin examples repository](http
|
||||||
|
|
||||||
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
### Using GET, POST, PUT, PATCH, DELETE and OPTIONS
|
||||||
|
|
||||||
|
For each HTTP method, there is a router method to set up handlers on specific paths.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
// Creates a gin router with default middleware:
|
// Creates a gin router with default middleware:
|
||||||
|
@ -217,8 +219,13 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that every GET handler will also receive its equivalent HEAD requests automatically, so
|
||||||
|
although you can define HEAD handlers explicitly, you don't really need to.
|
||||||
|
|
||||||
### Parameters in path
|
### Parameters in path
|
||||||
|
|
||||||
|
Path parameters take the form `:param` and accept any string separated the adjacent slash(es).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
|
@ -247,7 +254,10 @@ func main() {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Querystring parameters
|
### Query string parameters
|
||||||
|
|
||||||
|
The query string follows the `?` in the URL. The parameters are accessed using Gin `Context` methods
|
||||||
|
`Query` and `DefaultQuery`.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -315,7 +325,7 @@ func main() {
|
||||||
id: 1234; page: 1; name: manu; message: this_is_great
|
id: 1234; page: 1; name: manu; message: this_is_great
|
||||||
```
|
```
|
||||||
|
|
||||||
### Map as querystring or postform parameters
|
### Map as query string or post form parameters
|
||||||
|
|
||||||
```
|
```
|
||||||
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
|
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
|
||||||
|
@ -547,7 +557,7 @@ func main() {
|
||||||
c.String(200, "pong")
|
c.String(200, "pong")
|
||||||
})
|
})
|
||||||
|
|
||||||
router.Run(":8080")
|
router.Run(":8080")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -856,7 +856,11 @@ func (c *Context) Cookie(name string) (string, error) {
|
||||||
func (c *Context) Render(code int, r render.Render) {
|
func (c *Context) Render(code int, r render.Render) {
|
||||||
c.Status(code)
|
c.Status(code)
|
||||||
|
|
||||||
if !bodyAllowedForStatus(code) {
|
// Rendering is suppressed for 1xx, 204 and 304 and all HEAD requests
|
||||||
|
// This avoids wasteful response processing, even though the standard
|
||||||
|
// net/http response processing would discard the response entity in
|
||||||
|
// (some of) these cases.
|
||||||
|
if !bodyAllowedForStatus(code) || c.Request.Method == http.MethodHead {
|
||||||
r.WriteContentType(c.Writer)
|
r.WriteContentType(c.Writer)
|
||||||
c.Writer.WriteHeaderNow()
|
c.Writer.WriteHeaderNow()
|
||||||
return
|
return
|
||||||
|
|
|
@ -389,7 +389,7 @@ func TestContextHandler(t *testing.T) {
|
||||||
|
|
||||||
func TestContextQuery(t *testing.T) {
|
func TestContextQuery(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil)
|
c.Request, _ = http.NewRequest("GET", "/?foo=bar&page=10&id=", nil)
|
||||||
|
|
||||||
value, ok := c.GetQuery("foo")
|
value, ok := c.GetQuery("foo")
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
|
@ -675,6 +675,7 @@ func TestContextRenderPanicIfErr(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Render(http.StatusOK, &TestPanicRender{})
|
c.Render(http.StatusOK, &TestPanicRender{})
|
||||||
|
|
||||||
|
@ -687,6 +688,7 @@ func TestContextRenderPanicIfErr(t *testing.T) {
|
||||||
func TestContextRenderJSON(t *testing.T) {
|
func TestContextRenderJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
|
||||||
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
||||||
|
|
||||||
|
@ -740,6 +742,7 @@ func TestContextRenderNoContentJSON(t *testing.T) {
|
||||||
func TestContextRenderAPIJSON(t *testing.T) {
|
func TestContextRenderAPIJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "http://example.com/", nil)
|
||||||
|
|
||||||
c.Header("Content-Type", "application/vnd.api+json")
|
c.Header("Content-Type", "application/vnd.api+json")
|
||||||
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
c.JSON(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
@ -767,6 +770,7 @@ func TestContextRenderNoContentAPIJSON(t *testing.T) {
|
||||||
func TestContextRenderIndentedJSON(t *testing.T) {
|
func TestContextRenderIndentedJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
||||||
|
|
||||||
|
@ -779,6 +783,7 @@ func TestContextRenderIndentedJSON(t *testing.T) {
|
||||||
func TestContextRenderNoContentIndentedJSON(t *testing.T) {
|
func TestContextRenderNoContentIndentedJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}})
|
||||||
|
|
||||||
|
@ -792,6 +797,7 @@ func TestContextRenderNoContentIndentedJSON(t *testing.T) {
|
||||||
func TestContextRenderSecureJSON(t *testing.T) {
|
func TestContextRenderSecureJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, router := CreateTestContext(w)
|
c, router := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
router.SecureJsonPrefix("&&&START&&&")
|
router.SecureJsonPrefix("&&&START&&&")
|
||||||
c.SecureJSON(http.StatusCreated, []string{"foo", "bar"})
|
c.SecureJSON(http.StatusCreated, []string{"foo", "bar"})
|
||||||
|
@ -805,6 +811,7 @@ func TestContextRenderSecureJSON(t *testing.T) {
|
||||||
func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"})
|
c.SecureJSON(http.StatusNoContent, []string{"foo", "bar"})
|
||||||
|
|
||||||
|
@ -816,6 +823,7 @@ func TestContextRenderNoContentSecureJSON(t *testing.T) {
|
||||||
func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
|
c.AsciiJSON(http.StatusNoContent, []string{"lang", "Go语言"})
|
||||||
|
|
||||||
|
@ -830,6 +838,8 @@ func TestContextRenderNoContentAsciiJSON(t *testing.T) {
|
||||||
func TestContextRenderPureJSON(t *testing.T) {
|
func TestContextRenderPureJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
c.PureJSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"})
|
||||||
assert.Equal(t, http.StatusCreated, w.Code)
|
assert.Equal(t, http.StatusCreated, w.Code)
|
||||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
||||||
|
@ -841,6 +851,7 @@ func TestContextRenderPureJSON(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)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
@ -855,6 +866,7 @@ func TestContextRenderHTML(t *testing.T) {
|
||||||
func TestContextRenderHTML2(t *testing.T) {
|
func TestContextRenderHTML2(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, router := CreateTestContext(w)
|
c, router := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
// print debug warning log when Engine.trees > 0
|
// print debug warning log when Engine.trees > 0
|
||||||
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}})
|
||||||
|
@ -880,6 +892,7 @@ func TestContextRenderHTML2(t *testing.T) {
|
||||||
func TestContextRenderNoContentHTML(t *testing.T) {
|
func TestContextRenderNoContentHTML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, router := CreateTestContext(w)
|
c, router := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
|
@ -895,6 +908,7 @@ func TestContextRenderNoContentHTML(t *testing.T) {
|
||||||
func TestContextRenderXML(t *testing.T) {
|
func TestContextRenderXML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.XML(http.StatusCreated, H{"foo": "bar"})
|
c.XML(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
|
@ -907,6 +921,7 @@ func TestContextRenderXML(t *testing.T) {
|
||||||
func TestContextRenderNoContentXML(t *testing.T) {
|
func TestContextRenderNoContentXML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.XML(http.StatusNoContent, H{"foo": "bar"})
|
c.XML(http.StatusNoContent, H{"foo": "bar"})
|
||||||
|
|
||||||
|
@ -920,6 +935,7 @@ func TestContextRenderNoContentXML(t *testing.T) {
|
||||||
func TestContextRenderString(t *testing.T) {
|
func TestContextRenderString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.String(http.StatusCreated, "test %s %d", "string", 2)
|
c.String(http.StatusCreated, "test %s %d", "string", 2)
|
||||||
|
|
||||||
|
@ -932,6 +948,7 @@ func TestContextRenderString(t *testing.T) {
|
||||||
func TestContextRenderNoContentString(t *testing.T) {
|
func TestContextRenderNoContentString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.String(http.StatusNoContent, "test %s %d", "string", 2)
|
c.String(http.StatusNoContent, "test %s %d", "string", 2)
|
||||||
|
|
||||||
|
@ -945,6 +962,7 @@ func TestContextRenderNoContentString(t *testing.T) {
|
||||||
func TestContextRenderHTMLString(t *testing.T) {
|
func TestContextRenderHTMLString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3)
|
c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3)
|
||||||
|
@ -958,6 +976,7 @@ func TestContextRenderHTMLString(t *testing.T) {
|
||||||
func TestContextRenderNoContentHTMLString(t *testing.T) {
|
func TestContextRenderNoContentHTMLString(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||||
c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3)
|
c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3)
|
||||||
|
@ -972,6 +991,7 @@ func TestContextRenderNoContentHTMLString(t *testing.T) {
|
||||||
func TestContextRenderData(t *testing.T) {
|
func TestContextRenderData(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`))
|
c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`))
|
||||||
|
|
||||||
|
@ -984,6 +1004,7 @@ func TestContextRenderData(t *testing.T) {
|
||||||
func TestContextRenderNoContentData(t *testing.T) {
|
func TestContextRenderNoContentData(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`))
|
c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`))
|
||||||
|
|
||||||
|
@ -995,6 +1016,7 @@ func TestContextRenderNoContentData(t *testing.T) {
|
||||||
func TestContextRenderSSE(t *testing.T) {
|
func TestContextRenderSSE(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.SSEvent("float", 1.5)
|
c.SSEvent("float", 1.5)
|
||||||
c.Render(-1, sse.Event{
|
c.Render(-1, sse.Event{
|
||||||
|
@ -1012,6 +1034,7 @@ func TestContextRenderSSE(t *testing.T) {
|
||||||
func TestContextRenderFile(t *testing.T) {
|
func TestContextRenderFile(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("GET", "/", nil)
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
c.File("./gin.go")
|
c.File("./gin.go")
|
||||||
|
@ -1024,6 +1047,7 @@ func TestContextRenderFile(t *testing.T) {
|
||||||
func TestContextRenderFileFromFS(t *testing.T) {
|
func TestContextRenderFileFromFS(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("GET", "/some/path", nil)
|
c.Request, _ = http.NewRequest("GET", "/some/path", nil)
|
||||||
c.FileFromFS("./gin.go", Dir(".", false))
|
c.FileFromFS("./gin.go", Dir(".", false))
|
||||||
|
@ -1037,6 +1061,7 @@ func TestContextRenderFileFromFS(t *testing.T) {
|
||||||
func TestContextRenderAttachment(t *testing.T) {
|
func TestContextRenderAttachment(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
newFilename := "new_filename.go"
|
newFilename := "new_filename.go"
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("GET", "/", nil)
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
@ -1052,6 +1077,7 @@ func TestContextRenderAttachment(t *testing.T) {
|
||||||
func TestContextRenderYAML(t *testing.T) {
|
func TestContextRenderYAML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.YAML(http.StatusCreated, H{"foo": "bar"})
|
c.YAML(http.StatusCreated, H{"foo": "bar"})
|
||||||
|
|
||||||
|
@ -1066,6 +1092,7 @@ func TestContextRenderYAML(t *testing.T) {
|
||||||
func TestContextRenderProtoBuf(t *testing.T) {
|
func TestContextRenderProtoBuf(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
reps := []int64{int64(1), int64(2)}
|
reps := []int64{int64(1), int64(2)}
|
||||||
label := "test"
|
label := "test"
|
||||||
|
@ -1086,6 +1113,7 @@ func TestContextRenderProtoBuf(t *testing.T) {
|
||||||
|
|
||||||
func TestContextHeaders(t *testing.T) {
|
func TestContextHeaders(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
c.Header("Content-Type", "text/plain")
|
c.Header("Content-Type", "text/plain")
|
||||||
c.Header("X-Custom", "value")
|
c.Header("X-Custom", "value")
|
||||||
|
|
||||||
|
@ -1118,6 +1146,7 @@ func TestContextRenderRedirectWithRelativePath(t *testing.T) {
|
||||||
func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
|
func TestContextRenderRedirectWithAbsolutePath(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
||||||
c.Redirect(http.StatusFound, "http://google.com")
|
c.Redirect(http.StatusFound, "http://google.com")
|
||||||
|
@ -1131,7 +1160,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
c.Redirect(http.StatusCreated, "/resource")
|
c.Redirect(http.StatusCreated, "/resource")
|
||||||
c.Writer.WriteHeaderNow()
|
c.Writer.WriteHeaderNow()
|
||||||
|
|
||||||
|
@ -1141,7 +1170,7 @@ func TestContextRenderRedirectWith201(t *testing.T) {
|
||||||
|
|
||||||
func TestContextRenderRedirectAll(t *testing.T) {
|
func TestContextRenderRedirectAll(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
|
assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") })
|
||||||
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
|
assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") })
|
||||||
assert.Panics(t, func() { c.Redirect(299, "/resource") })
|
assert.Panics(t, func() { c.Redirect(299, "/resource") })
|
||||||
|
@ -1153,7 +1182,7 @@ func TestContextRenderRedirectAll(t *testing.T) {
|
||||||
func TestContextNegotiationWithJSON(t *testing.T) {
|
func TestContextNegotiationWithJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
|
|
||||||
c.Negotiate(http.StatusOK, Negotiate{
|
c.Negotiate(http.StatusOK, Negotiate{
|
||||||
Offered: []string{MIMEJSON, MIMEXML, MIMEYAML},
|
Offered: []string{MIMEJSON, MIMEXML, MIMEYAML},
|
||||||
|
@ -1168,7 +1197,7 @@ func TestContextNegotiationWithJSON(t *testing.T) {
|
||||||
func TestContextNegotiationWithXML(t *testing.T) {
|
func TestContextNegotiationWithXML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
|
|
||||||
c.Negotiate(http.StatusOK, Negotiate{
|
c.Negotiate(http.StatusOK, Negotiate{
|
||||||
Offered: []string{MIMEXML, MIMEJSON, MIMEYAML},
|
Offered: []string{MIMEXML, MIMEJSON, MIMEYAML},
|
||||||
|
@ -1183,7 +1212,7 @@ func TestContextNegotiationWithXML(t *testing.T) {
|
||||||
func TestContextNegotiationWithHTML(t *testing.T) {
|
func TestContextNegotiationWithHTML(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, router := CreateTestContext(w)
|
c, router := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
|
@ -1201,7 +1230,7 @@ func TestContextNegotiationWithHTML(t *testing.T) {
|
||||||
func TestContextNegotiationNotSupport(t *testing.T) {
|
func TestContextNegotiationNotSupport(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
|
|
||||||
c.Negotiate(http.StatusOK, Negotiate{
|
c.Negotiate(http.StatusOK, Negotiate{
|
||||||
Offered: []string{MIMEPOSTForm},
|
Offered: []string{MIMEPOSTForm},
|
||||||
|
@ -1214,7 +1243,7 @@ func TestContextNegotiationNotSupport(t *testing.T) {
|
||||||
|
|
||||||
func TestContextNegotiationFormat(t *testing.T) {
|
func TestContextNegotiationFormat(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||||
|
|
||||||
assert.Panics(t, func() { c.NegotiateFormat() })
|
assert.Panics(t, func() { c.NegotiateFormat() })
|
||||||
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
assert.Equal(t, MIMEJSON, c.NegotiateFormat(MIMEJSON, MIMEXML))
|
||||||
|
@ -1305,6 +1334,7 @@ type testJSONAbortMsg struct {
|
||||||
func TestContextAbortWithStatusJSON(t *testing.T) {
|
func TestContextAbortWithStatusJSON(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
c.index = 4
|
c.index = 4
|
||||||
|
|
||||||
in := new(testJSONAbortMsg)
|
in := new(testJSONAbortMsg)
|
||||||
|
@ -1365,6 +1395,7 @@ func TestContextError(t *testing.T) {
|
||||||
|
|
||||||
func TestContextTypedError(t *testing.T) {
|
func TestContextTypedError(t *testing.T) {
|
||||||
c, _ := CreateTestContext(httptest.NewRecorder())
|
c, _ := CreateTestContext(httptest.NewRecorder())
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) // nolint: errcheck
|
c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) // nolint: errcheck
|
||||||
c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) // nolint: errcheck
|
c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) // nolint: errcheck
|
||||||
|
|
||||||
|
@ -1380,6 +1411,7 @@ func TestContextTypedError(t *testing.T) {
|
||||||
func TestContextAbortWithError(t *testing.T) {
|
func TestContextAbortWithError(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") // nolint: errcheck
|
c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") // nolint: errcheck
|
||||||
|
|
||||||
|
@ -1537,7 +1569,7 @@ func TestContextBadAutoBind(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `json:"foo"`
|
Foo string `json:"foo"`
|
||||||
|
@ -1670,7 +1702,7 @@ func TestContextBadAutoShouldBind(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
|
||||||
c.Request, _ = http.NewRequest("POST", "http://example.com", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("\"foo\":\"bar\", \"bar\":\"foo\"}"))
|
||||||
c.Request.Header.Add("Content-Type", MIMEJSON)
|
c.Request.Header.Add("Content-Type", MIMEJSON)
|
||||||
var obj struct {
|
var obj struct {
|
||||||
Foo string `json:"foo"`
|
Foo string `json:"foo"`
|
||||||
|
@ -1734,7 +1766,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest(
|
c.Request, _ = http.NewRequest(
|
||||||
"POST", "http://example.com", bytes.NewBufferString(tt.bodyA),
|
"POST", "/", bytes.NewBufferString(tt.bodyA),
|
||||||
)
|
)
|
||||||
// When it binds to typeA and typeB, it finds the body is
|
// When it binds to typeA and typeB, it finds the body is
|
||||||
// not typeB but typeA.
|
// not typeB but typeA.
|
||||||
|
@ -1752,7 +1784,7 @@ func TestContextShouldBindBodyWith(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
c.Request, _ = http.NewRequest(
|
c.Request, _ = http.NewRequest(
|
||||||
"POST", "http://example.com", bytes.NewBufferString(tt.bodyB),
|
"POST", "/", bytes.NewBufferString(tt.bodyB),
|
||||||
)
|
)
|
||||||
objA := typeA{}
|
objA := typeA{}
|
||||||
assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
assert.Error(t, c.ShouldBindBodyWith(&objA, tt.bindingA))
|
||||||
|
@ -1825,6 +1857,7 @@ func TestContextGetRawData(t *testing.T) {
|
||||||
func TestContextRenderDataFromReader(t *testing.T) {
|
func TestContextRenderDataFromReader(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
body := "#!PNG some raw data"
|
body := "#!PNG some raw data"
|
||||||
reader := strings.NewReader(body)
|
reader := strings.NewReader(body)
|
||||||
|
@ -1844,6 +1877,7 @@ func TestContextRenderDataFromReader(t *testing.T) {
|
||||||
func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
|
func TestContextRenderDataFromReaderNoHeaders(t *testing.T) {
|
||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
c, _ := CreateTestContext(w)
|
c, _ := CreateTestContext(w)
|
||||||
|
c.Request, _ = http.NewRequest("GET", "/", nil)
|
||||||
|
|
||||||
body := "#!PNG some raw data"
|
body := "#!PNG some raw data"
|
||||||
reader := strings.NewReader(body)
|
reader := strings.NewReader(body)
|
||||||
|
|
75
gin.go
75
gin.go
|
@ -390,7 +390,6 @@ func (engine *Engine) HandleContext(c *Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (engine *Engine) handleHTTPRequest(c *Context) {
|
func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||||
httpMethod := c.Request.Method
|
|
||||||
rPath := c.Request.URL.Path
|
rPath := c.Request.URL.Path
|
||||||
unescape := false
|
unescape := false
|
||||||
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
|
||||||
|
@ -402,40 +401,20 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||||
rPath = cleanPath(rPath)
|
rPath = cleanPath(rPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find root of the tree for the given HTTP method
|
if engine.handleHTTPRequestForMethod(c, c.Request.Method, rPath, unescape) {
|
||||||
t := engine.trees
|
return
|
||||||
for i, tl := 0, len(t); i < tl; i++ {
|
}
|
||||||
if t[i].method != httpMethod {
|
|
||||||
continue
|
// For HEAD requests, reaching here means no HEAD handler had been set up, so we retry the
|
||||||
}
|
// equivalent GET handler as if this had been a GET request. As expected for HEAD requests,
|
||||||
root := t[i].root
|
// the response content will of course be empty (see net/http for implementation details).
|
||||||
// Find route in tree
|
if c.Request.Method == http.MethodHead && engine.handleHTTPRequestForMethod(c, http.MethodGet, rPath, unescape) {
|
||||||
value := root.getValue(rPath, c.params, unescape)
|
return
|
||||||
if value.params != nil {
|
|
||||||
c.Params = *value.params
|
|
||||||
}
|
|
||||||
if value.handlers != nil {
|
|
||||||
c.handlers = value.handlers
|
|
||||||
c.fullPath = value.fullPath
|
|
||||||
c.Next()
|
|
||||||
c.writermem.WriteHeaderNow()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if httpMethod != "CONNECT" && rPath != "/" {
|
|
||||||
if value.tsr && engine.RedirectTrailingSlash {
|
|
||||||
redirectTrailingSlash(c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if engine.HandleMethodNotAllowed {
|
if engine.HandleMethodNotAllowed {
|
||||||
for _, tree := range engine.trees {
|
for _, tree := range engine.trees {
|
||||||
if tree.method == httpMethod {
|
if tree.method == c.Request.Method {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
|
if value := tree.root.getValue(rPath, nil, unescape); value.handlers != nil {
|
||||||
|
@ -449,6 +428,40 @@ func (engine *Engine) handleHTTPRequest(c *Context) {
|
||||||
serveError(c, http.StatusNotFound, default404Body)
|
serveError(c, http.StatusNotFound, default404Body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (engine *Engine) handleHTTPRequestForMethod(c *Context, httpMethod, rPath string, unescape bool) bool {
|
||||||
|
// Find root of the tree for the given HTTP method
|
||||||
|
for _, t := range engine.trees {
|
||||||
|
if t.method != httpMethod {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
root := t.root
|
||||||
|
// Find route in tree
|
||||||
|
value := root.getValue(rPath, c.params, unescape)
|
||||||
|
if value.params != nil {
|
||||||
|
c.Params = *value.params
|
||||||
|
}
|
||||||
|
if value.handlers != nil {
|
||||||
|
c.handlers = value.handlers
|
||||||
|
c.fullPath = value.fullPath
|
||||||
|
c.Next()
|
||||||
|
c.writermem.WriteHeaderNow()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if httpMethod != "CONNECT" && rPath != "/" {
|
||||||
|
if value.tsr && engine.RedirectTrailingSlash {
|
||||||
|
redirectTrailingSlash(c)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return false // probably 404 or 406
|
||||||
|
}
|
||||||
|
|
||||||
var mimePlain = []string{MIMEPlain}
|
var mimePlain = []string{MIMEPlain}
|
||||||
|
|
||||||
func serveError(c *Context, code int, defaultMessage []byte) {
|
func serveError(c *Context, code int, defaultMessage []byte) {
|
||||||
|
|
22
ginS/gins.go
22
ginS/gins.go
|
@ -58,16 +58,23 @@ func Handle(httpMethod, relativePath string, handlers ...gin.HandlerFunc) gin.IR
|
||||||
return engine().Handle(httpMethod, relativePath, handlers...)
|
return engine().Handle(httpMethod, relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GET is a shortcut for router.Handle("GET", path, handle)
|
||||||
|
// HEAD requests will also be handled automatically by this handler.
|
||||||
|
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
|
return engine().GET(relativePath, handlers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
||||||
|
// This is rarely needed because every GET handler will also handle HEAD requests.
|
||||||
|
func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
|
return engine().HEAD(relativePath, handlers...)
|
||||||
|
}
|
||||||
|
|
||||||
// POST is a shortcut for router.Handle("POST", path, handle)
|
// POST is a shortcut for router.Handle("POST", path, handle)
|
||||||
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().POST(relativePath, handlers...)
|
return engine().POST(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET is a shortcut for router.Handle("GET", path, handle)
|
|
||||||
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
|
||||||
return engine().GET(relativePath, handlers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
// DELETE is a shortcut for router.Handle("DELETE", path, handle)
|
||||||
func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
func DELETE(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().DELETE(relativePath, handlers...)
|
return engine().DELETE(relativePath, handlers...)
|
||||||
|
@ -88,11 +95,6 @@ func OPTIONS(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().OPTIONS(relativePath, handlers...)
|
return engine().OPTIONS(relativePath, handlers...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle)
|
|
||||||
func HEAD(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
|
||||||
return engine().HEAD(relativePath, handlers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any is a wrapper for Engine.Any.
|
// Any is a wrapper for Engine.Any.
|
||||||
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
func Any(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
|
||||||
return engine().Any(relativePath, handlers...)
|
return engine().Any(relativePath, handlers...)
|
||||||
|
|
|
@ -450,7 +450,7 @@ func TestListOfRoutes(t *testing.T) {
|
||||||
|
|
||||||
list := router.Routes()
|
list := router.Routes()
|
||||||
|
|
||||||
assert.Len(t, list, 7)
|
assert.Len(t, list, 6)
|
||||||
assertRoutePresent(t, list, RouteInfo{
|
assertRoutePresent(t, list, RouteInfo{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Path: "/favicon.ico",
|
Path: "/favicon.ico",
|
||||||
|
|
|
@ -23,58 +23,58 @@ func TestLogger(t *testing.T) {
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
router := New()
|
router := New()
|
||||||
router.Use(LoggerWithWriter(buffer))
|
router.Use(LoggerWithWriter(buffer))
|
||||||
router.GET("/example", func(c *Context) {})
|
router.GET("/get", func(c *Context) {})
|
||||||
router.POST("/example", func(c *Context) {})
|
router.POST("/post", func(c *Context) {})
|
||||||
router.PUT("/example", func(c *Context) {})
|
router.PUT("/put", func(c *Context) {})
|
||||||
router.DELETE("/example", func(c *Context) {})
|
router.DELETE("/delete", func(c *Context) {})
|
||||||
router.PATCH("/example", func(c *Context) {})
|
router.PATCH("/patch", func(c *Context) {})
|
||||||
router.HEAD("/example", func(c *Context) {})
|
router.HEAD("/head", func(c *Context) {})
|
||||||
router.OPTIONS("/example", func(c *Context) {})
|
router.OPTIONS("/options", func(c *Context) {})
|
||||||
|
|
||||||
performRequest(router, "GET", "/example?a=100")
|
performRequest(router, "GET", "/get?a=100")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "GET")
|
assert.Contains(t, buffer.String(), "GET")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/get")
|
||||||
assert.Contains(t, buffer.String(), "a=100")
|
assert.Contains(t, buffer.String(), "a=100")
|
||||||
|
|
||||||
// I wrote these first (extending the above) but then realized they are more
|
// I wrote these first (extending the above) but then realized they are more
|
||||||
// like integration tests because they test the whole logging process rather
|
// like integration tests because they test the whole logging process rather
|
||||||
// than individual functions. Im not sure where these should go.
|
// than individual functions. Im not sure where these should go.
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "POST", "/example")
|
performRequest(router, "POST", "/post")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "POST")
|
assert.Contains(t, buffer.String(), "POST")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/post")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "PUT", "/example")
|
performRequest(router, "PUT", "/put")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "PUT")
|
assert.Contains(t, buffer.String(), "PUT")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/put")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "DELETE", "/example")
|
performRequest(router, "DELETE", "/delete")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "DELETE")
|
assert.Contains(t, buffer.String(), "DELETE")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/delete")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "PATCH", "/example")
|
performRequest(router, "PATCH", "/patch")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "PATCH")
|
assert.Contains(t, buffer.String(), "PATCH")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/patch")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "HEAD", "/example")
|
performRequest(router, "HEAD", "/head")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "HEAD")
|
assert.Contains(t, buffer.String(), "HEAD")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/head")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "OPTIONS", "/example")
|
performRequest(router, "OPTIONS", "/options")
|
||||||
assert.Contains(t, buffer.String(), "200")
|
assert.Contains(t, buffer.String(), "200")
|
||||||
assert.Contains(t, buffer.String(), "OPTIONS")
|
assert.Contains(t, buffer.String(), "OPTIONS")
|
||||||
assert.Contains(t, buffer.String(), "/example")
|
assert.Contains(t, buffer.String(), "/options")
|
||||||
|
|
||||||
buffer.Reset()
|
buffer.Reset()
|
||||||
performRequest(router, "GET", "/notfound")
|
performRequest(router, "GET", "/notfound")
|
||||||
|
|
|
@ -80,7 +80,7 @@ func (group *RouterGroup) handle(httpMethod, relativePath string, handlers Handl
|
||||||
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
|
// The last handler should be the real handler, the other ones should be middleware that can and should be shared among different routes.
|
||||||
// See the example code in GitHub.
|
// See the example code in GitHub.
|
||||||
//
|
//
|
||||||
// For GET, POST, PUT, PATCH and DELETE requests the respective shortcut
|
// For GET, HEAD, POST, PUT, PATCH, DELETE and OPTIONS requests the respective shortcut
|
||||||
// functions can be used.
|
// functions can be used.
|
||||||
//
|
//
|
||||||
// This function is intended for bulk loading and to allow the usage of less
|
// This function is intended for bulk loading and to allow the usage of less
|
||||||
|
@ -98,11 +98,18 @@ func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRo
|
||||||
return group.handle(http.MethodPost, relativePath, handlers)
|
return group.handle(http.MethodPost, relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET is a shortcut for router.Handle("GET", path, handle).
|
// GET is a shortcut for router.Handle("GET", path, handle). The equivalent
|
||||||
|
// HEAD requests will also be handled automatically by this handler.
|
||||||
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle(http.MethodGet, relativePath, handlers)
|
return group.handle(http.MethodGet, relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HEAD is a shortcut for router.Handle("HEAD", path, handle). This is rarely needed
|
||||||
|
// because every GET handler will also handle HEAD requests.
|
||||||
|
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
return group.handle(http.MethodHead, relativePath, handlers)
|
||||||
|
}
|
||||||
|
|
||||||
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
|
// DELETE is a shortcut for router.Handle("DELETE", path, handle).
|
||||||
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
return group.handle(http.MethodDelete, relativePath, handlers)
|
return group.handle(http.MethodDelete, relativePath, handlers)
|
||||||
|
@ -123,11 +130,6 @@ func (group *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc)
|
||||||
return group.handle(http.MethodOptions, relativePath, handlers)
|
return group.handle(http.MethodOptions, relativePath, handlers)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HEAD is a shortcut for router.Handle("HEAD", path, handle).
|
|
||||||
func (group *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) IRoutes {
|
|
||||||
return group.handle(http.MethodHead, relativePath, handlers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Any registers a route that matches all the HTTP methods.
|
// Any registers a route that matches all the HTTP methods.
|
||||||
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
|
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
|
||||||
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
|
||||||
|
@ -152,8 +154,9 @@ func (group *RouterGroup) StaticFile(relativePath, filepath string) IRoutes {
|
||||||
handler := func(c *Context) {
|
handler := func(c *Context) {
|
||||||
c.File(filepath)
|
c.File(filepath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register GET (and HEAD) handler
|
||||||
group.GET(relativePath, handler)
|
group.GET(relativePath, handler)
|
||||||
group.HEAD(relativePath, handler)
|
|
||||||
return group.returnObj()
|
return group.returnObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,9 +179,8 @@ func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRou
|
||||||
handler := group.createStaticHandler(relativePath, fs)
|
handler := group.createStaticHandler(relativePath, fs)
|
||||||
urlPattern := path.Join(relativePath, "/*filepath")
|
urlPattern := path.Join(relativePath, "/*filepath")
|
||||||
|
|
||||||
// Register GET and HEAD handlers
|
// Register GET (and HEAD) handler
|
||||||
group.GET(urlPattern, handler)
|
group.GET(urlPattern, handler)
|
||||||
group.HEAD(urlPattern, handler)
|
|
||||||
return group.returnObj()
|
return group.returnObj()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ func TestRouterGroupBasicHandle(t *testing.T) {
|
||||||
performRequestInGroup(t, http.MethodPut)
|
performRequestInGroup(t, http.MethodPut)
|
||||||
performRequestInGroup(t, http.MethodPatch)
|
performRequestInGroup(t, http.MethodPatch)
|
||||||
performRequestInGroup(t, http.MethodDelete)
|
performRequestInGroup(t, http.MethodDelete)
|
||||||
performRequestInGroup(t, http.MethodHead)
|
|
||||||
performRequestInGroup(t, http.MethodOptions)
|
performRequestInGroup(t, http.MethodOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,9 +69,6 @@ func performRequestInGroup(t *testing.T, method string) {
|
||||||
case http.MethodDelete:
|
case http.MethodDelete:
|
||||||
v1.DELETE("/test", handler)
|
v1.DELETE("/test", handler)
|
||||||
login.DELETE("/test", handler)
|
login.DELETE("/test", handler)
|
||||||
case http.MethodHead:
|
|
||||||
v1.HEAD("/test", handler)
|
|
||||||
login.HEAD("/test", handler)
|
|
||||||
case http.MethodOptions:
|
case http.MethodOptions:
|
||||||
v1.OPTIONS("/test", handler)
|
v1.OPTIONS("/test", handler)
|
||||||
login.OPTIONS("/test", handler)
|
login.OPTIONS("/test", handler)
|
||||||
|
@ -89,6 +85,30 @@ func performRequestInGroup(t *testing.T, method string) {
|
||||||
assert.Equal(t, "the method was "+method+" and index 1", w.Body.String())
|
assert.Equal(t, "the method was "+method+" and index 1", w.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouterGroupBasicHandleHEAD(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
v1 := router.Group("v1", func(c *Context) {})
|
||||||
|
assert.Equal(t, "/v1", v1.BasePath())
|
||||||
|
|
||||||
|
login := v1.Group("/login/", func(c *Context) {}, func(c *Context) {})
|
||||||
|
assert.Equal(t, "/v1/login/", login.BasePath())
|
||||||
|
|
||||||
|
handler := func(c *Context) {
|
||||||
|
c.String(http.StatusBadRequest, "the method was %s and index %d", c.Request.Method, c.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
v1.HEAD("/test", handler)
|
||||||
|
login.HEAD("/test", handler)
|
||||||
|
|
||||||
|
w := performRequest(router, http.MethodHead, "/v1/login/test")
|
||||||
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||||
|
assert.Equal(t, "", w.Body.String())
|
||||||
|
|
||||||
|
w = performRequest(router, http.MethodHead, "/v1/test")
|
||||||
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||||
|
assert.Equal(t, "", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRouterGroupInvalidStatic(t *testing.T) {
|
func TestRouterGroupInvalidStatic(t *testing.T) {
|
||||||
router := New()
|
router := New()
|
||||||
assert.Panics(t, func() {
|
assert.Panics(t, func() {
|
||||||
|
|
|
@ -368,6 +368,18 @@ func TestRouterMiddlewareAndStatic(t *testing.T) {
|
||||||
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRouteHeadUsesGetHandlerByDefault(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
router.GET("/path", func(c *Context) {
|
||||||
|
c.String(http.StatusOK, "responseText")
|
||||||
|
})
|
||||||
|
w := performRequest(router, http.MethodHead, "/path")
|
||||||
|
assert.Equal(t, http.StatusOK, w.Code)
|
||||||
|
// The response entity is blank because response rendering is suppressed for HEAD requests.
|
||||||
|
// Note that the Go net/http handlers would discard the entity for HEAD requests anyway.
|
||||||
|
assert.Equal(t, "", w.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestRouteNotAllowedEnabled(t *testing.T) {
|
func TestRouteNotAllowedEnabled(t *testing.T) {
|
||||||
router := New()
|
router := New()
|
||||||
router.HandleMethodNotAllowed = true
|
router.HandleMethodNotAllowed = true
|
||||||
|
@ -494,7 +506,7 @@ func TestRouterStaticFSNotFound(t *testing.T) {
|
||||||
assert.Equal(t, "non existent", w.Body.String())
|
assert.Equal(t, "non existent", w.Body.String())
|
||||||
|
|
||||||
w = performRequest(router, http.MethodHead, "/nonexistent")
|
w = performRequest(router, http.MethodHead, "/nonexistent")
|
||||||
assert.Equal(t, "non existent", w.Body.String())
|
assert.Equal(t, "", w.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRouterStaticFSFileNotFound(t *testing.T) {
|
func TestRouterStaticFSFileNotFound(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue