mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-21 16:58:08 +08:00
Merge branch 'master' into master-readme
This commit is contained in:
commit
57d6d2c8ed
38
README.md
38
README.md
@ -40,6 +40,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
||||
- [Only Bind Query String](#only-bind-query-string)
|
||||
- [Bind Query String or Post Data](#bind-query-string-or-post-data)
|
||||
- [Bind Uri](#bind-uri)
|
||||
- [Bind Header](#bind-header)
|
||||
- [Bind HTML checkboxes](#bind-html-checkboxes)
|
||||
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
|
||||
- [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
|
||||
@ -910,6 +911,43 @@ $ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
|
||||
$ curl -v localhost:8088/thinkerou/not-uuid
|
||||
```
|
||||
|
||||
### Bind Header
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type testHeader struct {
|
||||
Rate int `header:"Rate"`
|
||||
Domain string `header:"Domain"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
r.GET("/", func(c *gin.Context) {
|
||||
h := testHeader{}
|
||||
|
||||
if err := c.ShouldBindHeader(&h); err != nil {
|
||||
c.JSON(200, err)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n", h)
|
||||
c.JSON(200, gin.H{"Rate": h.Rate, "Domain": h.Domain})
|
||||
})
|
||||
|
||||
r.Run()
|
||||
|
||||
// client
|
||||
// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/
|
||||
// output
|
||||
// {"Domain":"music","Rate":300}
|
||||
}
|
||||
```
|
||||
|
||||
### Bind HTML checkboxes
|
||||
|
||||
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
|
||||
|
@ -78,6 +78,7 @@ var (
|
||||
MsgPack = msgpackBinding{}
|
||||
YAML = yamlBinding{}
|
||||
Uri = uriBinding{}
|
||||
Header = headerBinding{}
|
||||
)
|
||||
|
||||
// Default returns the appropriate Binding instance based on the HTTP method
|
||||
|
@ -667,6 +667,31 @@ func TestExistsFails(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestHeaderBinding(t *testing.T) {
|
||||
h := Header
|
||||
assert.Equal(t, "header", h.Name())
|
||||
|
||||
type tHeader struct {
|
||||
Limit int `header:"limit"`
|
||||
}
|
||||
|
||||
var theader tHeader
|
||||
req := requestWithBody("GET", "/", "")
|
||||
req.Header.Add("limit", "1000")
|
||||
assert.NoError(t, h.Bind(req, &theader))
|
||||
assert.Equal(t, 1000, theader.Limit)
|
||||
|
||||
req = requestWithBody("GET", "/", "")
|
||||
req.Header.Add("fail", `{fail:fail}`)
|
||||
|
||||
type failStruct struct {
|
||||
Fail map[string]interface{} `header:"fail"`
|
||||
}
|
||||
|
||||
err := h.Bind(req, &failStruct{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestUriBinding(t *testing.T) {
|
||||
b := Uri
|
||||
assert.Equal(t, "uri", b.Name())
|
||||
|
34
binding/header.go
Normal file
34
binding/header.go
Normal file
@ -0,0 +1,34 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type headerBinding struct{}
|
||||
|
||||
func (headerBinding) Name() string {
|
||||
return "header"
|
||||
}
|
||||
|
||||
func (headerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||
|
||||
if err := mapHeader(obj, req.Header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return validate(obj)
|
||||
}
|
||||
|
||||
func mapHeader(ptr interface{}, h map[string][]string) error {
|
||||
return mappingByPtr(ptr, headerSource(h), "header")
|
||||
}
|
||||
|
||||
type headerSource map[string][]string
|
||||
|
||||
var _ setter = headerSource(nil)
|
||||
|
||||
func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
|
||||
return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt)
|
||||
}
|
10
context.go
10
context.go
@ -583,6 +583,11 @@ func (c *Context) BindYAML(obj interface{}) error {
|
||||
return c.MustBindWith(obj, binding.YAML)
|
||||
}
|
||||
|
||||
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
|
||||
func (c *Context) BindHeader(obj interface{}) error {
|
||||
return c.MustBindWith(obj, binding.Header)
|
||||
}
|
||||
|
||||
// BindUri binds the passed struct pointer using binding.Uri.
|
||||
// It will abort the request with HTTP 400 if any error occurs.
|
||||
func (c *Context) BindUri(obj interface{}) error {
|
||||
@ -637,6 +642,11 @@ func (c *Context) ShouldBindYAML(obj interface{}) error {
|
||||
return c.ShouldBindWith(obj, binding.YAML)
|
||||
}
|
||||
|
||||
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
|
||||
func (c *Context) ShouldBindHeader(obj interface{}) error {
|
||||
return c.ShouldBindWith(obj, binding.Header)
|
||||
}
|
||||
|
||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||
func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||
m := make(map[string][]string)
|
||||
|
@ -1436,6 +1436,28 @@ func TestContextBindWithXML(t *testing.T) {
|
||||
assert.Equal(t, 0, w.Body.Len())
|
||||
}
|
||||
|
||||
func TestContextBindHeader(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
c.Request.Header.Add("rate", "8000")
|
||||
c.Request.Header.Add("domain", "music")
|
||||
c.Request.Header.Add("limit", "1000")
|
||||
|
||||
var testHeader struct {
|
||||
Rate int `header:"Rate"`
|
||||
Domain string `header:"Domain"`
|
||||
Limit int `header:"limit"`
|
||||
}
|
||||
|
||||
assert.NoError(t, c.BindHeader(&testHeader))
|
||||
assert.Equal(t, 8000, testHeader.Rate)
|
||||
assert.Equal(t, "music", testHeader.Domain)
|
||||
assert.Equal(t, 1000, testHeader.Limit)
|
||||
assert.Equal(t, 0, w.Body.Len())
|
||||
}
|
||||
|
||||
func TestContextBindWithQuery(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
@ -1543,6 +1565,28 @@ func TestContextShouldBindWithXML(t *testing.T) {
|
||||
assert.Equal(t, 0, w.Body.Len())
|
||||
}
|
||||
|
||||
func TestContextShouldBindHeader(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
||||
c.Request, _ = http.NewRequest("POST", "/", nil)
|
||||
c.Request.Header.Add("rate", "8000")
|
||||
c.Request.Header.Add("domain", "music")
|
||||
c.Request.Header.Add("limit", "1000")
|
||||
|
||||
var testHeader struct {
|
||||
Rate int `header:"Rate"`
|
||||
Domain string `header:"Domain"`
|
||||
Limit int `header:"limit"`
|
||||
}
|
||||
|
||||
assert.NoError(t, c.ShouldBindHeader(&testHeader))
|
||||
assert.Equal(t, 8000, testHeader.Rate)
|
||||
assert.Equal(t, "music", testHeader.Domain)
|
||||
assert.Equal(t, 1000, testHeader.Limit)
|
||||
assert.Equal(t, 0, w.Body.Len())
|
||||
}
|
||||
|
||||
func TestContextShouldBindWithQuery(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
Loading…
x
Reference in New Issue
Block a user