mirror of
https://github.com/gin-gonic/gin.git
synced 2025-10-15 21:06:39 +08:00
Merge 26c932b14546985ea5387c57afcd5fbe1d0d87e0 into 857db39f82fb82456af2906ccea972ae1d65ff57
This commit is contained in:
commit
3f01464295
@ -197,6 +197,18 @@ func setByForm(value reflect.Value, field reflect.StructField, form map[string][
|
||||
}
|
||||
}
|
||||
|
||||
type jsonUnmarshaler interface {
|
||||
UnmarshalJSON([]byte) error
|
||||
}
|
||||
|
||||
type textUnmarshaler interface {
|
||||
UnmarshalText([]byte) error
|
||||
}
|
||||
|
||||
type binaryUnmarshaler interface {
|
||||
UnmarshalBinary([]byte) error
|
||||
}
|
||||
|
||||
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
|
||||
switch value.Kind() {
|
||||
case reflect.Int:
|
||||
@ -236,6 +248,15 @@ func setWithProperType(val string, value reflect.Value, field reflect.StructFiel
|
||||
case time.Time:
|
||||
return setTimeField(val, field, value)
|
||||
}
|
||||
if unmarshaler, ok := value.Addr().Interface().(jsonUnmarshaler); ok {
|
||||
return unmarshaler.UnmarshalJSON(bytesconv.StringToBytes(val))
|
||||
}
|
||||
if unmarshaler, ok := value.Addr().Interface().(textUnmarshaler); ok {
|
||||
return unmarshaler.UnmarshalText(bytesconv.StringToBytes(val))
|
||||
}
|
||||
if unmarshaler, ok := value.Addr().Interface().(binaryUnmarshaler); ok {
|
||||
return unmarshaler.UnmarshalBinary(bytesconv.StringToBytes(val))
|
||||
}
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
case reflect.Map:
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
|
||||
|
@ -5,7 +5,9 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -321,3 +323,93 @@ func TestMappingIgnoredCircularRef(t *testing.T) {
|
||||
err := mappingByPtr(&s, formSource{}, "form")
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// this structure has special json unmarshaller, in order to parse email (as an example) as specific structure
|
||||
type withJsonUnmarshaller struct {
|
||||
Name string
|
||||
Host string
|
||||
}
|
||||
|
||||
func (o *withJsonUnmarshaller) UnmarshalJSON(data []byte) error {
|
||||
elems := strings.Split(string(data), "@")
|
||||
if len(elems) != 2 {
|
||||
return errors.New("cannot parse %q as email")
|
||||
}
|
||||
o.Name = elems[0]
|
||||
o.Host = elems[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMappingStructFieldJSONUnmarshaller(t *testing.T) {
|
||||
var s struct {
|
||||
Email withJsonUnmarshaller
|
||||
}
|
||||
|
||||
err := mappingByPtr(&s, formSource{"Email": {`test@example.org`}}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", s.Email.Name)
|
||||
assert.Equal(t, "example.org", s.Email.Host)
|
||||
|
||||
err = mappingByPtr(&s, formSource{"Email": {`not an email`}}, "form")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// this structure has special text unmarshaller, in order to parse email (as an example) as specific structure
|
||||
type withTextUnmarshaller struct {
|
||||
Name string
|
||||
Host string
|
||||
}
|
||||
|
||||
func (o *withTextUnmarshaller) UnmarshalText(data []byte) error {
|
||||
elems := strings.Split(string(data), "@")
|
||||
if len(elems) != 2 {
|
||||
return errors.New("cannot parse %q as email")
|
||||
}
|
||||
o.Name = elems[0]
|
||||
o.Host = elems[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMappingStructFieldTextUnmarshaller(t *testing.T) {
|
||||
var s struct {
|
||||
Email withTextUnmarshaller
|
||||
}
|
||||
|
||||
err := mappingByPtr(&s, formSource{"Email": {`test@example.org`}}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", s.Email.Name)
|
||||
assert.Equal(t, "example.org", s.Email.Host)
|
||||
|
||||
err = mappingByPtr(&s, formSource{"Email": {`not an email`}}, "form")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
// this structure has special binary unmarshaller, in order to parse email (as an example) as specific structure
|
||||
type withBinaryUnmarshaller struct {
|
||||
Name string
|
||||
Host string
|
||||
}
|
||||
|
||||
func (o *withBinaryUnmarshaller) UnmarshalBinary(data []byte) error {
|
||||
elems := strings.Split(string(data), "@")
|
||||
if len(elems) != 2 {
|
||||
return errors.New("cannot parse %q as email")
|
||||
}
|
||||
o.Name = elems[0]
|
||||
o.Host = elems[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMappingStructFieldBinaryUnmarshaller(t *testing.T) {
|
||||
var s struct {
|
||||
Email withBinaryUnmarshaller
|
||||
}
|
||||
|
||||
err := mappingByPtr(&s, formSource{"Email": {`test@example.org`}}, "form")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", s.Email.Name)
|
||||
assert.Equal(t, "example.org", s.Email.Host)
|
||||
|
||||
err = mappingByPtr(&s, formSource{"Email": {`not an email`}}, "form")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
60
docs/doc.md
60
docs/doc.md
@ -57,6 +57,7 @@
|
||||
- [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
|
||||
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
|
||||
- [Bind form-data request with custom struct and custom tag](#bind-form-data-request-with-custom-struct-and-custom-tag)
|
||||
- [Bind Query with custom unmarshalers](#bind-query-with-custom-unmarshalers)
|
||||
- [http2 server push](#http2-server-push)
|
||||
- [Define format for the log of routes](#define-format-for-the-log-of-routes)
|
||||
- [Set and get a cookie](#set-and-get-a-cookie)
|
||||
@ -1155,7 +1156,7 @@ func main() {
|
||||
router.StaticFS("/more_static", http.Dir("my_file_system"))
|
||||
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
|
||||
router.StaticFileFS("/more_favicon.ico", "more_favicon.ico", http.Dir("my_file_system"))
|
||||
|
||||
|
||||
// Listen and serve on 0.0.0.0:8080
|
||||
router.Run(":8080")
|
||||
}
|
||||
@ -2002,6 +2003,59 @@ func ListHandler(s *Service) func(ctx *gin.Context) {
|
||||
}
|
||||
```
|
||||
|
||||
### Bind Query with custom unmarshalers
|
||||
|
||||
Any structure that has custom `UnmarshalJSON` or `UnmarshalText` or `UnmarshalBinary` can be used to parse input as necessary
|
||||
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
// Booking contains data binded using custom unmarshaler.
|
||||
type Payload struct {
|
||||
Email EmailDetails `form:"email"`
|
||||
}
|
||||
// this structure has special json unmarshaller, in order to parse email (as an example) as specific structure
|
||||
type EmailDetails struct {
|
||||
Name string
|
||||
Host string
|
||||
}
|
||||
func (o *EmailDetails) UnmarshalJSON(data []byte) error {
|
||||
elems := strings.Split(string(data), "@")
|
||||
if len(elems) != 2 {
|
||||
return fmt.Errorf("cannot parse %q as email", string(data))
|
||||
}
|
||||
o.Name = elems[0]
|
||||
o.Host = elems[1]
|
||||
return nil
|
||||
}
|
||||
func main() {
|
||||
route := gin.Default()
|
||||
route.GET("/email", getEmail)
|
||||
route.Run(":8085")
|
||||
}
|
||||
func getEmail(c *gin.Context) {
|
||||
var p Payload
|
||||
if err := c.ShouldBindQuery(&p); err == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "Email information is correct"})
|
||||
} else {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```console
|
||||
$ curl "localhost:8085/email?email=test@example.org"
|
||||
{"message":"Email information is correct"}
|
||||
|
||||
$ curl "localhost:8085/email?email=test-something-else"
|
||||
{"error":"cannot parse \"test-something-else\" as email"}
|
||||
```
|
||||
|
||||
### http2 server push
|
||||
|
||||
http.Pusher is supported only **go1.8+**. See the [golang blog](https://go.dev/blog/h2push) for detail information.
|
||||
@ -2134,7 +2188,7 @@ or network CIDRs from where clients which their request headers related to clien
|
||||
IP can be trusted. They can be IPv4 addresses, IPv4 CIDRs, IPv6 addresses or
|
||||
IPv6 CIDRs.
|
||||
|
||||
**Attention:** Gin trust all proxies by default if you don't specify a trusted
|
||||
**Attention:** Gin trust all proxies by default if you don't specify a trusted
|
||||
proxy using the function above, **this is NOT safe**. At the same time, if you don't
|
||||
use any proxy, you can disable this feature by using `Engine.SetTrustedProxies(nil)`,
|
||||
then `Context.ClientIP()` will return the remote address directly to avoid some
|
||||
@ -2163,7 +2217,7 @@ func main() {
|
||||
```
|
||||
|
||||
**Notice:** If you are using a CDN service, you can set the `Engine.TrustedPlatform`
|
||||
to skip TrustedProxies check, it has a higher priority than TrustedProxies.
|
||||
to skip TrustedProxies check, it has a higher priority than TrustedProxies.
|
||||
Look at the example below:
|
||||
|
||||
```go
|
||||
|
Loading…
x
Reference in New Issue
Block a user