From 35e38db7715879cd2b68c44340dc296757c752c9 Mon Sep 17 00:00:00 2001 From: Rock Date: Fri, 1 Oct 2021 14:32:06 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20Add=20function=20to=20override?= =?UTF-8?q?=20form=20decoder=20setting=20(#1100)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 add function to overide form decoder setting 🔥 Feature : SetBodyParserDecoder map all option to form decoder, use with BodyParserConfig, BodyParserType 🚨 Test : Test_Ctx_BodyParser_WithSetBodyParserDecoder * 🔥 Use decoder builder function with default setting on init decoderPool --- ctx.go | 41 +++++++++++++++++++++++++++++++++++++--- ctx_test.go | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/ctx.go b/ctx.go index 4a3f088944..af551711be 100644 --- a/ctx.go +++ b/ctx.go @@ -86,6 +86,21 @@ type Views interface { Render(io.Writer, string, interface{}, ...string) error } +// BodyParserType require two element, type and converter for register. +// Use BodyParserType with BodyParser for parsing custom type in form data. +type BodyParserType struct { + Customtype interface{} + Converter func(string) reflect.Value +} + +// BodyParserConfig form decoder config for SetBodyParserDecoder +type BodyParserConfig struct { + IgnoreUnknownKeys bool + SetAliasTag string + BodyParserType []BodyParserType + ZeroEmpty bool +} + // AcquireCtx retrieves a new Ctx from the pool. func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { c := app.pool.Get().(*Ctx) @@ -272,11 +287,31 @@ func (c *Ctx) Body() []byte { // decoderPool helps to improve BodyParser's and QueryParser's performance var decoderPool = &sync.Pool{New: func() interface{} { + return decoderBuilder(BodyParserConfig{ + IgnoreUnknownKeys: true, + ZeroEmpty: true, + }) +}} + +// SetBodyParserDecoder allow globally change the option of form decoder, update decoderPool +func SetBodyParserDecoder(bodyParserConfig BodyParserConfig) { + decoderPool = &sync.Pool{New: func() interface{} { + return decoderBuilder(bodyParserConfig) + }} +} + +func decoderBuilder(bodyParserConfig BodyParserConfig) interface{} { var decoder = schema.NewDecoder() - decoder.ZeroEmpty(true) - decoder.IgnoreUnknownKeys(true) + decoder.IgnoreUnknownKeys(bodyParserConfig.IgnoreUnknownKeys) + if bodyParserConfig.SetAliasTag != "" { + decoder.SetAliasTag(bodyParserConfig.SetAliasTag) + } + for _, v := range bodyParserConfig.BodyParserType { + decoder.RegisterConverter(reflect.ValueOf(v.Customtype).Interface(), v.Converter) + } + decoder.ZeroEmpty(bodyParserConfig.ZeroEmpty) return decoder -}} +} // BodyParser binds the request body to a struct. // It supports decoding the following content types based on the Content-Type header: diff --git a/ctx_test.go b/ctx_test.go index 91ab336623..c33745e6da 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -399,6 +399,60 @@ func Test_Ctx_BodyParser(t *testing.T) { testDecodeParserError(MIMEMultipartForm+`;boundary="b"`, "--b") } +// go test -run Test_Ctx_BodyParser_WithSetBodyParserDecoder +func Test_Ctx_BodyParser_WithSetBodyParserDecoder(t *testing.T) { + type CustomTime time.Time + + var timeConverter = func(value string) reflect.Value { + if v, err := time.Parse("2006-01-02", value); err == nil { + return reflect.ValueOf(v) + } + return reflect.Value{} + } + + customTime := BodyParserType{ + Customtype: CustomTime{}, + Converter: timeConverter, + } + + SetBodyParserDecoder(BodyParserConfig{ + IgnoreUnknownKeys: true, + BodyParserType: []BodyParserType{customTime}, + ZeroEmpty: true, + SetAliasTag: "form", + }) + + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + type Demo struct { + Date CustomTime `form:"date"` + Title string `form:"title"` + Body string `form:"body"` + } + + testDecodeParser := func(contentType, body string) { + c.Request().Header.SetContentType(contentType) + c.Request().SetBody([]byte(body)) + c.Request().Header.SetContentLength(len(body)) + d := Demo{ + Title: "Existing title", + Body: "Existing Body", + } + utils.AssertEqual(t, nil, c.BodyParser(&d)) + date := fmt.Sprintf("%v", d.Date) + fmt.Println(date, d.Title, d.Body) + utils.AssertEqual(t, "{0 63743587200 }", date) + utils.AssertEqual(t, "", d.Title) + utils.AssertEqual(t, "New Body", d.Body) + } + + testDecodeParser(MIMEApplicationForm, "date=2020-12-15&title=&body=New Body") + testDecodeParser(MIMEMultipartForm+`; boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"date\"\r\n\r\n2020-12-15\r\n--b\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n\r\n--b\r\nContent-Disposition: form-data; name=\"body\"\r\n\r\nNew Body\r\n--b--") +} + // go test -v -run=^$ -bench=Benchmark_Ctx_BodyParser_JSON -benchmem -count=4 func Benchmark_Ctx_BodyParser_JSON(b *testing.B) { app := New()