@ -84,6 +84,7 @@ var (
 | 
			
		||||
	YAML          BindingBody = yamlBinding{}
 | 
			
		||||
	Uri           BindingUri  = uriBinding{}
 | 
			
		||||
	Header        Binding     = headerBinding{}
 | 
			
		||||
	Plain         BindingBody = plainBinding{}
 | 
			
		||||
	TOML          BindingBody = tomlBinding{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,7 @@ var (
 | 
			
		||||
	Uri           = uriBinding{}
 | 
			
		||||
	Header        = headerBinding{}
 | 
			
		||||
	TOML          = tomlBinding{}
 | 
			
		||||
	Plain         = plainBinding{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Default returns the appropriate Binding instance based on the HTTP method
 | 
			
		||||
 | 
			
		||||
@ -1342,6 +1342,46 @@ func (h hook) Read([]byte) (int, error) {
 | 
			
		||||
	return 0, errors.New("error")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type failRead struct{}
 | 
			
		||||
 | 
			
		||||
func (f *failRead) Read(b []byte) (n int, err error) {
 | 
			
		||||
	return 0, errors.New("my fail")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *failRead) Close() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPlainBinding(t *testing.T) {
 | 
			
		||||
	p := Plain
 | 
			
		||||
	assert.Equal(t, "plain", p.Name())
 | 
			
		||||
 | 
			
		||||
	var s string
 | 
			
		||||
	req := requestWithBody("POST", "/", "test string")
 | 
			
		||||
	assert.NoError(t, p.Bind(req, &s))
 | 
			
		||||
	assert.Equal(t, s, "test string")
 | 
			
		||||
 | 
			
		||||
	var bs []byte
 | 
			
		||||
	req = requestWithBody("POST", "/", "test []byte")
 | 
			
		||||
	assert.NoError(t, p.Bind(req, &bs))
 | 
			
		||||
	assert.Equal(t, bs, []byte("test []byte"))
 | 
			
		||||
 | 
			
		||||
	var i int
 | 
			
		||||
	req = requestWithBody("POST", "/", "test fail")
 | 
			
		||||
	assert.Error(t, p.Bind(req, &i))
 | 
			
		||||
 | 
			
		||||
	req = requestWithBody("POST", "/", "")
 | 
			
		||||
	req.Body = &failRead{}
 | 
			
		||||
	assert.Error(t, p.Bind(req, &s))
 | 
			
		||||
 | 
			
		||||
	req = requestWithBody("POST", "/", "")
 | 
			
		||||
	assert.Nil(t, p.Bind(req, nil))
 | 
			
		||||
 | 
			
		||||
	var ptr *string
 | 
			
		||||
	req = requestWithBody("POST", "/", "")
 | 
			
		||||
	assert.Nil(t, p.Bind(req, ptr))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func testProtoBodyBindingFail(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
 | 
			
		||||
	assert.Equal(t, name, b.Name())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										56
									
								
								binding/plain.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								binding/plain.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
			
		||||
package binding
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"reflect"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-gonic/gin/internal/bytesconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type plainBinding struct{}
 | 
			
		||||
 | 
			
		||||
func (plainBinding) Name() string {
 | 
			
		||||
	return "plain"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (plainBinding) Bind(req *http.Request, obj interface{}) error {
 | 
			
		||||
	all, err := io.ReadAll(req.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return decodePlain(all, obj)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (plainBinding) BindBody(body []byte, obj any) error {
 | 
			
		||||
	return decodePlain(body, obj)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decodePlain(data []byte, obj any) error {
 | 
			
		||||
	if obj == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v := reflect.ValueOf(obj)
 | 
			
		||||
 | 
			
		||||
	for v.Kind() == reflect.Ptr {
 | 
			
		||||
		if v.IsNil() {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		v = v.Elem()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Kind() == reflect.String {
 | 
			
		||||
		v.SetString(bytesconv.BytesToString(data))
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := v.Interface().([]byte); ok {
 | 
			
		||||
		v.SetBytes(data)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fmt.Errorf("type (%T) unknown type", v)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								context.go
									
									
									
									
									
								
							@ -614,7 +614,7 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error
 | 
			
		||||
	}
 | 
			
		||||
	defer src.Close()
 | 
			
		||||
 | 
			
		||||
	if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil {
 | 
			
		||||
	if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -667,6 +667,11 @@ func (c *Context) BindTOML(obj any) error {
 | 
			
		||||
	return c.MustBindWith(obj, binding.TOML)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BindPlain is a shortcut for c.MustBindWith(obj, binding.Plain).
 | 
			
		||||
func (c *Context) BindPlain(obj any) error {
 | 
			
		||||
	return c.MustBindWith(obj, binding.Plain)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
 | 
			
		||||
func (c *Context) BindHeader(obj any) error {
 | 
			
		||||
	return c.MustBindWith(obj, binding.Header)
 | 
			
		||||
@ -732,6 +737,11 @@ func (c *Context) ShouldBindTOML(obj any) error {
 | 
			
		||||
	return c.ShouldBindWith(obj, binding.TOML)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldBindPlain is a shortcut for c.ShouldBindWith(obj, binding.Plain).
 | 
			
		||||
func (c *Context) ShouldBindPlain(obj any) error {
 | 
			
		||||
	return c.ShouldBindWith(obj, binding.Plain)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
 | 
			
		||||
func (c *Context) ShouldBindHeader(obj any) error {
 | 
			
		||||
	return c.ShouldBindWith(obj, binding.Header)
 | 
			
		||||
@ -794,6 +804,11 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error {
 | 
			
		||||
	return c.ShouldBindBodyWith(obj, binding.TOML)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON).
 | 
			
		||||
func (c *Context) ShouldBindBodyWithPlain(obj any) error {
 | 
			
		||||
	return c.ShouldBindBodyWith(obj, binding.Plain)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClientIP implements one best effort algorithm to return the real client IP.
 | 
			
		||||
// It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
 | 
			
		||||
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										124
									
								
								context_test.go
									
									
									
									
									
								
							
							
						
						
									
										124
									
								
								context_test.go
									
									
									
									
									
								
							@ -1670,6 +1670,31 @@ func TestContextBindWithXML(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestContextBindPlain(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	// string
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	c, _ := CreateTestContext(w)
 | 
			
		||||
	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`))
 | 
			
		||||
	c.Request.Header.Add("Content-Type", MIMEPlain)
 | 
			
		||||
 | 
			
		||||
	var s string
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, c.BindPlain(&s))
 | 
			
		||||
	assert.Equal(t, "test string", s)
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
 | 
			
		||||
	// []byte
 | 
			
		||||
	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`))
 | 
			
		||||
	c.Request.Header.Add("Content-Type", MIMEPlain)
 | 
			
		||||
 | 
			
		||||
	var bs []byte
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, c.BindPlain(&bs))
 | 
			
		||||
	assert.Equal(t, []byte("test []byte"), bs)
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestContextBindHeader(t *testing.T) {
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	c, _ := CreateTestContext(w)
 | 
			
		||||
@ -1816,6 +1841,31 @@ func TestContextShouldBindWithXML(t *testing.T) {
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestContextShouldBindPlain(t *testing.T) {
 | 
			
		||||
	// string
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	c, _ := CreateTestContext(w)
 | 
			
		||||
	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test string`))
 | 
			
		||||
	c.Request.Header.Add("Content-Type", MIMEPlain)
 | 
			
		||||
 | 
			
		||||
	var s string
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, c.ShouldBindPlain(&s))
 | 
			
		||||
	assert.Equal(t, "test string", s)
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
	// []byte
 | 
			
		||||
 | 
			
		||||
	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(`test []byte`))
 | 
			
		||||
	c.Request.Header.Add("Content-Type", MIMEPlain)
 | 
			
		||||
 | 
			
		||||
	var bs []byte
 | 
			
		||||
 | 
			
		||||
	assert.NoError(t, c.ShouldBindPlain(&bs))
 | 
			
		||||
	assert.Equal(t, []byte("test []byte"), bs)
 | 
			
		||||
	assert.Equal(t, 0, w.Body.Len())
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestContextShouldBindHeader(t *testing.T) {
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	c, _ := CreateTestContext(w)
 | 
			
		||||
@ -2247,6 +2297,80 @@ func TestContextShouldBindBodyWithTOML(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestContextShouldBindBodyWithPlain(t *testing.T) {
 | 
			
		||||
	for _, tt := range []struct {
 | 
			
		||||
		name        string
 | 
			
		||||
		bindingBody binding.BindingBody
 | 
			
		||||
		body        string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:        " JSON & JSON-BODY ",
 | 
			
		||||
			bindingBody: binding.JSON,
 | 
			
		||||
			body:        `{"foo":"FOO"}`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        " JSON & XML-BODY ",
 | 
			
		||||
			bindingBody: binding.XML,
 | 
			
		||||
			body: `<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
<root>
 | 
			
		||||
<foo>FOO</foo>
 | 
			
		||||
</root>`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        " JSON & YAML-BODY ",
 | 
			
		||||
			bindingBody: binding.YAML,
 | 
			
		||||
			body:        `foo: FOO`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        " JSON & TOM-BODY ",
 | 
			
		||||
			bindingBody: binding.TOML,
 | 
			
		||||
			body:        `foo=FOO`,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:        " JSON & Plain-BODY ",
 | 
			
		||||
			bindingBody: binding.Plain,
 | 
			
		||||
			body:        `foo=FOO`,
 | 
			
		||||
		},
 | 
			
		||||
	} {
 | 
			
		||||
		t.Logf("testing: %s", tt.name)
 | 
			
		||||
 | 
			
		||||
		w := httptest.NewRecorder()
 | 
			
		||||
		c, _ := CreateTestContext(w)
 | 
			
		||||
 | 
			
		||||
		c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString(tt.body))
 | 
			
		||||
 | 
			
		||||
		type typeJSON struct {
 | 
			
		||||
			Foo string `json:"foo" binding:"required"`
 | 
			
		||||
		}
 | 
			
		||||
		objJSON := typeJSON{}
 | 
			
		||||
 | 
			
		||||
		if tt.bindingBody == binding.Plain {
 | 
			
		||||
			body := ""
 | 
			
		||||
			assert.NoError(t, c.ShouldBindBodyWithPlain(&body))
 | 
			
		||||
			assert.Equal(t, body, "foo=FOO")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if tt.bindingBody == binding.JSON {
 | 
			
		||||
			assert.NoError(t, c.ShouldBindBodyWithJSON(&objJSON))
 | 
			
		||||
			assert.Equal(t, typeJSON{"FOO"}, objJSON)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if tt.bindingBody == binding.XML {
 | 
			
		||||
			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 | 
			
		||||
			assert.Equal(t, typeJSON{}, objJSON)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if tt.bindingBody == binding.YAML {
 | 
			
		||||
			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 | 
			
		||||
			assert.Equal(t, typeJSON{}, objJSON)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if tt.bindingBody == binding.TOML {
 | 
			
		||||
			assert.Error(t, c.ShouldBindBodyWithJSON(&objJSON))
 | 
			
		||||
			assert.Equal(t, typeJSON{}, objJSON)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
func TestContextGolangContext(t *testing.T) {
 | 
			
		||||
	c, _ := CreateTestContext(httptest.NewRecorder())
 | 
			
		||||
	c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}"))
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user