replaced the default binder with rest.MultiBinder
This commit is contained in:
@@ -68,8 +68,8 @@ func (s *Serializer) Serialize(c echo.Context, i any, indent string) error {
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// data := map[string]any{"a": 1, "b": 2, "c": map[string]any{"c1": 11, "c2": 22}}
|
||||
// PickFields(data, "a,c.c1") // map[string]any{"a": 1, "c": map[string]any{"c1": 11}}
|
||||
// data := map[string]any{"a": 1, "b": 2, "c": map[string]any{"c1": 11, "c2": 22}}
|
||||
// PickFields(data, "a,c.c1") // map[string]any{"a": 1, "c": map[string]any{"c1": 11}}
|
||||
func PickFields(data any, rawFields string) (any, error) {
|
||||
parsedFields, err := parseFields(rawFields)
|
||||
if err != nil {
|
||||
|
||||
@@ -13,9 +13,35 @@ import (
|
||||
)
|
||||
|
||||
// MultipartJsonKey is the key for the special multipart/form-data
|
||||
// handling allowing reading serialized json payload without normalizations
|
||||
// handling allowing reading serialized json payload without normalization.
|
||||
const MultipartJsonKey string = "@jsonPayload"
|
||||
|
||||
// MultiBinder is similar to [echo.DefaultBinder] but uses slightly different
|
||||
// application/json and multipart/form-data bind methods to accommodate better
|
||||
// the PocketBase router needs.
|
||||
type MultiBinder struct{}
|
||||
|
||||
// Bind implements the [Binder.Bind] method.
|
||||
//
|
||||
// Bind is almost identical to [echo.DefaultBinder.Bind] but uses the
|
||||
// [rest.BindBody] function for binding the request body.
|
||||
func (b *MultiBinder) Bind(c echo.Context, i interface{}) (err error) {
|
||||
if err := echo.BindPathParams(c, i); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Only bind query parameters for GET/DELETE/HEAD to avoid unexpected behavior with destination struct binding from body.
|
||||
// For example a request URL `&id=1&lang=en` with body `{"id":100,"lang":"de"}` would lead to precedence issues.
|
||||
method := c.Request().Method
|
||||
if method == http.MethodGet || method == http.MethodDelete || method == http.MethodHead {
|
||||
if err = echo.BindQueryParams(c, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return BindBody(c, i)
|
||||
}
|
||||
|
||||
// BindBody binds request body content to i.
|
||||
//
|
||||
// This is similar to `echo.BindBody()`, but for JSON requests uses
|
||||
@@ -47,8 +73,8 @@ func BindBody(c echo.Context, i any) error {
|
||||
func CopyJsonBody(r *http.Request, i any) error {
|
||||
body := r.Body
|
||||
|
||||
// this usually shouldn't be needed because the Server calls close for us
|
||||
// but we are changing the request body with a new reader
|
||||
// this usually shouldn't be needed because the Server calls close
|
||||
// for us but we are changing the request body with a new reader
|
||||
defer body.Close()
|
||||
|
||||
limitReader := io.LimitReader(body, DefaultMaxMemory)
|
||||
@@ -82,7 +108,7 @@ func bindFormData(c echo.Context, i any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// special case to allow submitting json without normalizations
|
||||
// special case to allow submitting json without normalization
|
||||
// alongside the other multipart/form-data values
|
||||
jsonPayloadValues := values[MultipartJsonKey]
|
||||
for _, payload := range jsonPayloadValues {
|
||||
|
||||
@@ -13,6 +13,46 @@ import (
|
||||
"github.com/pocketbase/pocketbase/tools/rest"
|
||||
)
|
||||
|
||||
func TestMultiBinderBind(t *testing.T) {
|
||||
binder := rest.MultiBinder{}
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/test?query=123", strings.NewReader(`{"body":"456"}`))
|
||||
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
e := echo.New()
|
||||
e.Any("/:name", func(c echo.Context) error {
|
||||
// bind twice to ensure that the json body reader copy is invoked
|
||||
for i := 0; i < 2; i++ {
|
||||
data := struct {
|
||||
Name string `param:"name"`
|
||||
Query string `query:"query"`
|
||||
Body string `form:"body"`
|
||||
}{}
|
||||
|
||||
if err := binder.Bind(c, &data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if data.Name != "test" {
|
||||
t.Fatalf("Expected Name %q, got %q", "test", data.Name)
|
||||
}
|
||||
|
||||
if data.Query != "123" {
|
||||
t.Fatalf("Expected Query %q, got %q", "123", data.Query)
|
||||
}
|
||||
|
||||
if data.Body != "456" {
|
||||
t.Fatalf("Expected Body %q, got %q", "456", data.Body)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
e.ServeHTTP(rec, req)
|
||||
}
|
||||
|
||||
func TestBindBody(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
body io.Reader
|
||||
|
||||
Reference in New Issue
Block a user