mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-17 05:42:09 +08:00
feat: add methods c.BindCookie(obj)
from cookie and c.BindRequest(obj)
from http request
This commit is contained in:
parent
6ebb945bd7
commit
14f40ade40
72
README.md
72
README.md
@ -47,6 +47,8 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi
|
|||||||
- [Bind Query String or Post Data](#bind-query-string-or-post-data)
|
- [Bind Query String or Post Data](#bind-query-string-or-post-data)
|
||||||
- [Bind Uri](#bind-uri)
|
- [Bind Uri](#bind-uri)
|
||||||
- [Bind Header](#bind-header)
|
- [Bind Header](#bind-header)
|
||||||
|
- [Bind Cookie](#bind-cookie)
|
||||||
|
- [Bind Request](#bind-request)
|
||||||
- [Bind HTML checkboxes](#bind-html-checkboxes)
|
- [Bind HTML checkboxes](#bind-html-checkboxes)
|
||||||
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
|
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
|
||||||
- [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
|
- [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering)
|
||||||
@ -663,10 +665,10 @@ Note that you need to set the corresponding binding tag on all fields you want t
|
|||||||
|
|
||||||
Also, Gin provides two sets of methods for binding:
|
Also, Gin provides two sets of methods for binding:
|
||||||
- **Type** - Must bind
|
- **Type** - Must bind
|
||||||
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`
|
- **Methods** - `Bind`, `BindJSON`, `BindXML`, `BindQuery`, `BindYAML`, `BindHeader`, `BindCookie`, `BindRequest`
|
||||||
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
|
- **Behavior** - These methods use `MustBindWith` under the hood. If there is a binding error, the request is aborted with `c.AbortWithError(400, err).SetType(ErrorTypeBind)`. This sets the response status code to 400 and the `Content-Type` header is set to `text/plain; charset=utf-8`. Note that if you try to set the response code after this, it will result in a warning `[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422`. If you wish to have greater control over the behavior, consider using the `ShouldBind` equivalent method.
|
||||||
- **Type** - Should bind
|
- **Type** - Should bind
|
||||||
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`
|
- **Methods** - `ShouldBind`, `ShouldBindJSON`, `ShouldBindXML`, `ShouldBindQuery`, `ShouldBindYAML`, `ShouldBindHeader`, `ShouldBindCookie`, `ShouldBindReqeust`
|
||||||
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
|
- **Behavior** - These methods use `ShouldBindWith` under the hood. If there is a binding error, the error is returned and it is the developer's responsibility to handle the request and error appropriately.
|
||||||
|
|
||||||
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
|
When using the Bind-method, Gin tries to infer the binder depending on the Content-Type header. If you are sure what you are binding, you can use `MustBindWith` or `ShouldBindWith`.
|
||||||
@ -991,6 +993,72 @@ func main() {
|
|||||||
// output
|
// output
|
||||||
// {"Domain":"music","Rate":300}
|
// {"Domain":"music","Rate":300}
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
### Bind Cookie
|
||||||
|
|
||||||
|
see `Authorization` in `Params` of [Bind Request](#bind-request)
|
||||||
|
|
||||||
|
|
||||||
|
### Bind Request
|
||||||
|
|
||||||
|
`c.BindRequest` and `c.ShouldBindRquest` can bind all wanted values into struct instance at once.
|
||||||
|
Now,
|
||||||
|
+ supports values from `form`, `uri`, `header`, `cookie`
|
||||||
|
and request `req.Body`.
|
||||||
|
+ `req.Body` decocder is decided by header `Content-Type` value, see more [request_test.go](./binding/binding_request_test.go#L40)
|
||||||
|
|
||||||
|
**example**
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
Name string `uri:"name"`
|
||||||
|
Age int `form:"age,default=18"`
|
||||||
|
Money int32 `form:"money" binding:"required"`
|
||||||
|
Authorization string `cookie:"Authorization"`
|
||||||
|
UserAgent string `header:"User-Agent"`
|
||||||
|
Data struct {
|
||||||
|
Replicas *int32 `json:"replicas" yaml:"replicas" xml:"replicas" form:"replicas"`
|
||||||
|
} `body:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
r := gin.Default()
|
||||||
|
|
||||||
|
r.POST("/hello/:name", handler)
|
||||||
|
_ = r.Run(":9881")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func handler(c *gin.Context) {
|
||||||
|
var err error
|
||||||
|
params := &Params{}
|
||||||
|
|
||||||
|
err = c.ShouldBindRequest(params)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(200, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### POST test by RestClient of vscode extenstion
|
||||||
|
// POST http://127.0.0.1:9881/hello/zhangsan?money=1000
|
||||||
|
// Content-Type: application/json
|
||||||
|
// Accept-Language: en-GB,en-US;q=0.8,en;q=0.6,zh-CN;q=0.4
|
||||||
|
// Cookie: Authorization=auth123123;
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// "replicas":5
|
||||||
|
// }
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Bind HTML checkboxes
|
### Bind HTML checkboxes
|
||||||
|
@ -30,6 +30,7 @@ const (
|
|||||||
type Binding interface {
|
type Binding interface {
|
||||||
Name() string
|
Name() string
|
||||||
Bind(*http.Request, interface{}) error
|
Bind(*http.Request, interface{}) error
|
||||||
|
BindOnly(*http.Request, interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
|
||||||
@ -37,6 +38,7 @@ type Binding interface {
|
|||||||
type BindingBody interface {
|
type BindingBody interface {
|
||||||
Binding
|
Binding
|
||||||
BindBody([]byte, interface{}) error
|
BindBody([]byte, interface{}) error
|
||||||
|
BindBodyOnly([]byte, interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
|
||||||
@ -83,6 +85,8 @@ var (
|
|||||||
YAML = yamlBinding{}
|
YAML = yamlBinding{}
|
||||||
Uri = uriBinding{}
|
Uri = uriBinding{}
|
||||||
Header = headerBinding{}
|
Header = headerBinding{}
|
||||||
|
Request = requestBinding{}
|
||||||
|
Cookie = cookieBinding{}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default returns the appropriate Binding instance based on the HTTP method
|
// Default returns the appropriate Binding instance based on the HTTP method
|
||||||
|
91
binding/binding_request_test.go
Normal file
91
binding/binding_request_test.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Params struct {
|
||||||
|
Name string `uri:"name"`
|
||||||
|
Age int `form:"age,default=18"`
|
||||||
|
Money int32 `form:"money" binding:"required"`
|
||||||
|
Authorization string `cookie:"Authorization"`
|
||||||
|
UserAgent string `header:"User-Agent"`
|
||||||
|
Data struct {
|
||||||
|
Replicas *int32 `json:"replicas" yaml:"replicas" xml:"replicas" form:"replicas"`
|
||||||
|
} `body:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindRequest(t *testing.T) {
|
||||||
|
h := Request
|
||||||
|
assert.Equal(t, "request", h.Name())
|
||||||
|
|
||||||
|
path := "/hello/:name?money=1000"
|
||||||
|
mocks := []struct {
|
||||||
|
body string
|
||||||
|
mime string
|
||||||
|
method string
|
||||||
|
}{
|
||||||
|
{body: `replicas: 5`, mime: MIMEYAML, method: http.MethodPost},
|
||||||
|
{body: `{"replicas": 5}`, mime: MIMEJSON, method: http.MethodPut},
|
||||||
|
{
|
||||||
|
body: `<?xml version="1.0" encoding="UTF-8" ?><map><replicas>5</replicas></map>`,
|
||||||
|
mime: MIMEXML2,
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
body: `<map><replicas>5</replicas></map>`,
|
||||||
|
mime: MIMEXML,
|
||||||
|
method: http.MethodDelete,
|
||||||
|
},
|
||||||
|
{body: `replicas=5`, mime: MIMEPOSTForm, method: http.MethodPatch},
|
||||||
|
{body: `replicas=5`, mime: MIMEPOSTForm, method: http.MethodGet},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mock := range mocks {
|
||||||
|
path := path
|
||||||
|
|
||||||
|
t.Run(fmt.Sprintf("%s_%s", mock.method, mock.mime), func(t *testing.T) {
|
||||||
|
|
||||||
|
req := httpRequest(mock.method, path, mock.body)
|
||||||
|
mockUri := map[string][]string{
|
||||||
|
"name": {"zhangsan"},
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", mock.mime)
|
||||||
|
req.Header.Add("User-Agent", "go-client")
|
||||||
|
req.AddCookie(
|
||||||
|
&http.Cookie{Name: "Authorization", Value: "token 123123123"},
|
||||||
|
)
|
||||||
|
|
||||||
|
params := &Params{}
|
||||||
|
err := h.Bind(params, req, mockUri)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "zhangsan", params.Name) // uri
|
||||||
|
assert.Equal(t, 18, params.Age) // form,defualt
|
||||||
|
assert.Equal(t, int32(1000), params.Money) // form,required
|
||||||
|
assert.Equal(t, "token 123123123", params.Authorization) // cookie
|
||||||
|
assert.Equal(t, "go-client", params.UserAgent) // header
|
||||||
|
assert.Equal(t, int32(5), *params.Data.Replicas) // body,ptr
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func httpRequest(method string, path string, body string) *http.Request {
|
||||||
|
|
||||||
|
if method == http.MethodGet {
|
||||||
|
path = fmt.Sprintf("%s&%s", path, body)
|
||||||
|
req, _ := http.NewRequest(method, path, nil)
|
||||||
|
|
||||||
|
return req
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestWithBody(method, path, body)
|
||||||
|
}
|
@ -785,14 +785,14 @@ func TestUriBinding(t *testing.T) {
|
|||||||
var tag Tag
|
var tag Tag
|
||||||
m := make(map[string][]string)
|
m := make(map[string][]string)
|
||||||
m["name"] = []string{"thinkerou"}
|
m["name"] = []string{"thinkerou"}
|
||||||
assert.NoError(t, b.BindUri(m, &tag))
|
assert.NoError(t, b.Bind(m, &tag))
|
||||||
assert.Equal(t, "thinkerou", tag.Name)
|
assert.Equal(t, "thinkerou", tag.Name)
|
||||||
|
|
||||||
type NotSupportStruct struct {
|
type NotSupportStruct struct {
|
||||||
Name map[string]interface{} `uri:"name"`
|
Name map[string]interface{} `uri:"name"`
|
||||||
}
|
}
|
||||||
var not NotSupportStruct
|
var not NotSupportStruct
|
||||||
assert.Error(t, b.BindUri(m, ¬))
|
assert.Error(t, b.Bind(m, ¬))
|
||||||
assert.Equal(t, map[string]interface{}(nil), not.Name)
|
assert.Equal(t, map[string]interface{}(nil), not.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,7 +813,7 @@ func TestUriInnerBinding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tag Tag
|
var tag Tag
|
||||||
assert.NoError(t, Uri.BindUri(m, &tag))
|
assert.NoError(t, Uri.Bind(m, &tag))
|
||||||
assert.Equal(t, tag.Name, expectedName)
|
assert.Equal(t, tag.Name, expectedName)
|
||||||
assert.Equal(t, tag.S.Age, expectedAge)
|
assert.Equal(t, tag.S.Age, expectedAge)
|
||||||
}
|
}
|
||||||
|
27
binding/cookie.go
Normal file
27
binding/cookie.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type cookieBinding struct{}
|
||||||
|
|
||||||
|
func (cookieBinding) Name() string {
|
||||||
|
return "cookie"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b cookieBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cookieBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
|
cookies := req.Cookies()
|
||||||
|
|
||||||
|
form := make(map[string][]string, len(cookies))
|
||||||
|
for i := 0; i < len(cookies); i++ {
|
||||||
|
form[cookies[i].Name] = []string{cookies[i].Value}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapFormByTag(obj, form, "cookie")
|
||||||
|
}
|
@ -18,7 +18,14 @@ func (formBinding) Name() string {
|
|||||||
return "form"
|
return "form"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b formBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b formBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
if err := req.ParseForm(); err != nil {
|
if err := req.ParseForm(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -28,28 +35,42 @@ func (formBinding) Bind(req *http.Request, obj interface{}) error {
|
|||||||
if err := mapForm(obj, req.Form); err != nil {
|
if err := mapForm(obj, req.Form); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formPostBinding) Name() string {
|
func (formPostBinding) Name() string {
|
||||||
return "form-urlencoded"
|
return "form-urlencoded"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b formPostBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b formPostBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
if err := req.ParseForm(); err != nil {
|
if err := req.ParseForm(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := mapForm(obj, req.PostForm); err != nil {
|
if err := mapForm(obj, req.PostForm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formMultipartBinding) Name() string {
|
func (formMultipartBinding) Name() string {
|
||||||
return "multipart/form-data"
|
return "multipart/form-data"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b formMultipartBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
if err := req.ParseMultipartForm(defaultMemory); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -57,5 +78,5 @@ func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return validate(obj)
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -12,15 +12,19 @@ func (headerBinding) Name() string {
|
|||||||
return "header"
|
return "header"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (headerBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b headerBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
|
||||||
if err := mapHeader(obj, req.Header); err != nil {
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return validate(obj)
|
return validate(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (headerBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
|
return mapHeader(obj, req.Header)
|
||||||
|
}
|
||||||
|
|
||||||
func mapHeader(ptr interface{}, h map[string][]string) error {
|
func mapHeader(ptr interface{}, h map[string][]string) error {
|
||||||
return mappingByPtr(ptr, headerSource(h), "header")
|
return mappingByPtr(ptr, headerSource(h), "header")
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,33 @@ func (jsonBinding) Name() string {
|
|||||||
return "json"
|
return "json"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b jsonBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
if req == nil || req.Body == nil {
|
if req == nil || req.Body == nil {
|
||||||
return errors.New("invalid request")
|
return errors.New("invalid request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data, _ := ioutil.ReadAll(req.Body)
|
||||||
|
// fmt.Printf("%s", data)
|
||||||
|
|
||||||
return decodeJSON(req.Body, obj)
|
return decodeJSON(req.Body, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (jsonBinding) BindBody(body []byte, obj interface{}) error {
|
func (b jsonBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
if err := b.BindBodyOnly(body, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
func (b jsonBinding) BindBodyOnly(body []byte, obj interface{}) error {
|
||||||
return decodeJSON(bytes.NewReader(body), obj)
|
return decodeJSON(bytes.NewReader(body), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,5 +71,5 @@ func decodeJSON(r io.Reader, obj interface{}) error {
|
|||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,18 +21,31 @@ func (msgpackBinding) Name() string {
|
|||||||
return "msgpack"
|
return "msgpack"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b msgpackBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b msgpackBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
return decodeMsgPack(req.Body, obj)
|
return decodeMsgPack(req.Body, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msgpackBinding) BindBody(body []byte, obj interface{}) error {
|
func (b msgpackBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
if err := b.BindBodyOnly(body, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b msgpackBinding) BindBodyOnly(body []byte, obj interface{}) error {
|
||||||
return decodeMsgPack(bytes.NewReader(body), obj)
|
return decodeMsgPack(bytes.NewReader(body), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeMsgPack(r io.Reader, obj interface{}) error {
|
func decodeMsgPack(r io.Reader, obj interface{}) error {
|
||||||
cdc := new(codec.MsgpackHandle)
|
cdc := new(codec.MsgpackHandle)
|
||||||
if err := codec.NewDecoder(r, cdc).Decode(&obj); err != nil {
|
return codec.NewDecoder(r, cdc).Decode(&obj)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,22 @@ func (protobufBinding) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b protobufBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
return b.BindOnly(req, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b protobufBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
buf, err := ioutil.ReadAll(req.Body)
|
buf, err := ioutil.ReadAll(req.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return b.BindBody(buf, obj)
|
return b.BindBodyOnly(buf, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (protobufBinding) BindBody(body []byte, obj interface{}) error {
|
func (b protobufBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
return b.BindBodyOnly(body, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (protobufBinding) BindBodyOnly(body []byte, obj interface{}) error {
|
||||||
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -12,10 +12,18 @@ func (queryBinding) Name() string {
|
|||||||
return "query"
|
return "query"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (queryBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b queryBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queryBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
values := req.URL.Query()
|
values := req.URL.Query()
|
||||||
if err := mapForm(obj, values); err != nil {
|
if err := mapForm(obj, values); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
return nil
|
||||||
}
|
}
|
||||||
|
79
binding/request.go
Normal file
79
binding/request.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package binding
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type requestBinding struct{}
|
||||||
|
|
||||||
|
func (requestBinding) Name() string {
|
||||||
|
return "request"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b requestBinding) Bind(obj interface{}, req *http.Request, form map[string][]string) error {
|
||||||
|
if err := b.BindOnly(obj, req, form); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b requestBinding) BindOnly(obj interface{}, req *http.Request, uriMap map[string][]string) error {
|
||||||
|
|
||||||
|
if err := Uri.BindOnly(uriMap, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
binders := []interface{}{Header, Query, Cookie}
|
||||||
|
for _, binder := range binders {
|
||||||
|
if b, ok := binder.(Binding); ok {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// body decode
|
||||||
|
bodyObj := extractBody(obj)
|
||||||
|
if bodyObj == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// default json
|
||||||
|
contentType := req.Header.Get("Content-Type")
|
||||||
|
if contentType == "" {
|
||||||
|
contentType = MIMEJSON
|
||||||
|
}
|
||||||
|
bb := Default(req.Method, contentType)
|
||||||
|
return bb.BindOnly(req, bodyObj)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractBody return body object
|
||||||
|
func extractBody(obj interface{}) interface{} {
|
||||||
|
|
||||||
|
// pre-check obj
|
||||||
|
rv := reflect.ValueOf(obj)
|
||||||
|
rv = reflect.Indirect(rv)
|
||||||
|
if rv.Kind() != reflect.Struct {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := rv.Type()
|
||||||
|
for i := 0; i < rv.NumField(); i++ {
|
||||||
|
tf := typ.Field(i)
|
||||||
|
vf := rv.Field(i)
|
||||||
|
_, ok := tf.Tag.Lookup("body")
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// find body struct
|
||||||
|
if reflect.Indirect(vf).Kind() == reflect.Struct {
|
||||||
|
return vf.Addr().Interface()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -10,9 +10,13 @@ func (uriBinding) Name() string {
|
|||||||
return "uri"
|
return "uri"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uriBinding) BindUri(m map[string][]string, obj interface{}) error {
|
func (uriBinding) Bind(m map[string][]string, obj interface{}) error {
|
||||||
if err := mapUri(obj, m); err != nil {
|
if err := mapUri(obj, m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
return validate(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (uriBinding) BindOnly(m map[string][]string, obj interface{}) error {
|
||||||
|
return mapUri(obj, m)
|
||||||
|
}
|
||||||
|
@ -18,16 +18,35 @@ func (xmlBinding) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
func (xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
err := decodeXML(req.Body, obj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xmlBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
return decodeXML(req.Body, obj)
|
return decodeXML(req.Body, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (xmlBinding) BindBody(body []byte, obj interface{}) error {
|
func (b xmlBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
if err := b.BindBodyOnly(body, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (xmlBinding) BindBodyOnly(body []byte, obj interface{}) error {
|
||||||
return decodeXML(bytes.NewReader(body), obj)
|
return decodeXML(bytes.NewReader(body), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeXML(r io.Reader, obj interface{}) error {
|
func decodeXML(r io.Reader, obj interface{}) error {
|
||||||
decoder := xml.NewDecoder(r)
|
decoder := xml.NewDecoder(r)
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return validate(obj)
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -18,18 +18,31 @@ func (yamlBinding) Name() string {
|
|||||||
return "yaml"
|
return "yaml"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (yamlBinding) Bind(req *http.Request, obj interface{}) error {
|
func (b yamlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
|
if err := b.BindOnly(req, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) BindOnly(req *http.Request, obj interface{}) error {
|
||||||
return decodeYAML(req.Body, obj)
|
return decodeYAML(req.Body, obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (yamlBinding) BindBody(body []byte, obj interface{}) error {
|
func (b yamlBinding) BindBody(body []byte, obj interface{}) error {
|
||||||
|
if err := b.BindBodyOnly(body, obj); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return validate(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (yamlBinding) BindBodyOnly(body []byte, obj interface{}) error {
|
||||||
return decodeYAML(bytes.NewReader(body), obj)
|
return decodeYAML(bytes.NewReader(body), obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
func decodeYAML(r io.Reader, obj interface{}) error {
|
func decodeYAML(r io.Reader, obj interface{}) error {
|
||||||
decoder := yaml.NewDecoder(r)
|
decoder := yaml.NewDecoder(r)
|
||||||
if err := decoder.Decode(obj); err != nil {
|
return decoder.Decode(obj)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return validate(obj)
|
|
||||||
}
|
}
|
||||||
|
49
context.go
49
context.go
@ -631,6 +631,11 @@ func (c *Context) BindHeader(obj interface{}) error {
|
|||||||
return c.MustBindWith(obj, binding.Header)
|
return c.MustBindWith(obj, binding.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BindCookie is a shortcut for c.MustBindWith(obj, binding.Cookie).
|
||||||
|
func (c *Context) BindCookie(obj interface{}) error {
|
||||||
|
return c.MustBindWith(obj, binding.Cookie)
|
||||||
|
}
|
||||||
|
|
||||||
// BindUri binds the passed struct pointer using binding.Uri.
|
// BindUri binds the passed struct pointer using binding.Uri.
|
||||||
// It will abort the request with HTTP 400 if any error occurs.
|
// It will abort the request with HTTP 400 if any error occurs.
|
||||||
func (c *Context) BindUri(obj interface{}) error {
|
func (c *Context) BindUri(obj interface{}) error {
|
||||||
@ -641,6 +646,16 @@ func (c *Context) BindUri(obj interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BindRequest binds the passed struct pointer using binding.Request.
|
||||||
|
// It will abort the request with HTTP 400 if any error occurs.
|
||||||
|
func (c *Context) BindRequest(obj interface{}) error {
|
||||||
|
if err := c.ShouldBindRequest(obj); err != nil {
|
||||||
|
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
// MustBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
// It will abort the request with HTTP 400 if any error occurs.
|
// It will abort the request with HTTP 400 if any error occurs.
|
||||||
// See the binding package.
|
// See the binding package.
|
||||||
@ -690,13 +705,45 @@ func (c *Context) ShouldBindHeader(obj interface{}) error {
|
|||||||
return c.ShouldBindWith(obj, binding.Header)
|
return c.ShouldBindWith(obj, binding.Header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldBindCookie is a shortcut for c.ShouldBindWith(obj, binding.Cookie).
|
||||||
|
func (c *Context) ShouldBindCookie(obj interface{}) error {
|
||||||
|
return c.ShouldBindWith(obj, binding.Cookie)
|
||||||
|
}
|
||||||
|
|
||||||
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
// ShouldBindUri binds the passed struct pointer using the specified binding engine.
|
||||||
func (c *Context) ShouldBindUri(obj interface{}) error {
|
func (c *Context) ShouldBindUri(obj interface{}) error {
|
||||||
m := make(map[string][]string)
|
m := make(map[string][]string)
|
||||||
for _, v := range c.Params {
|
for _, v := range c.Params {
|
||||||
m[v.Key] = []string{v.Value}
|
m[v.Key] = []string{v.Value}
|
||||||
}
|
}
|
||||||
return binding.Uri.BindUri(m, obj)
|
return binding.Uri.Bind(m, obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShouldBindRequest binds the passed struct pointer using the specified binding engine.
|
||||||
|
// including
|
||||||
|
// `uri`,
|
||||||
|
// `query`
|
||||||
|
// `header` and
|
||||||
|
// `body data` with tag `body:"body"`
|
||||||
|
// and it's decoder is decided by header `Content-Type` value
|
||||||
|
//
|
||||||
|
// type Params struct {
|
||||||
|
// Name string `uri:"name"`
|
||||||
|
// Age int `form:"age,default=18"`
|
||||||
|
// Money int32 `form:"money" binding:"required"`
|
||||||
|
// Authorization string `cookie:"Authorization"`
|
||||||
|
// UserAgent string `header:"User-Agent"`
|
||||||
|
// Data struct {
|
||||||
|
// Replicas *int32 `json:"replicas" yaml:"replicas" xml:"replicas" form:"replicas"`
|
||||||
|
// } `body:"body"`
|
||||||
|
// }
|
||||||
|
func (c *Context) ShouldBindRequest(obj interface{}) error {
|
||||||
|
params := make(map[string][]string)
|
||||||
|
for _, v := range c.Params {
|
||||||
|
params[v.Key] = []string{v.Value}
|
||||||
|
}
|
||||||
|
|
||||||
|
return binding.Request.Bind(obj, c.Request, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
// ShouldBindWith binds the passed struct pointer using the specified binding engine.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user