🔥 Add function to override form decoder setting (#1100)

* 🔥 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
pull/1556/head
Rock 2021-10-01 14:32:06 +08:00 committed by GitHub
parent 26c29e248f
commit 35e38db771
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 4 deletions

43
ctx.go
View File

@ -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,12 +287,32 @@ func (c *Ctx) Body() []byte {
// decoderPool helps to improve BodyParser's and QueryParser's performance
var decoderPool = &sync.Pool{New: func() interface{} {
var decoder = schema.NewDecoder()
decoder.ZeroEmpty(true)
decoder.IgnoreUnknownKeys(true)
return decoder
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.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:
// application/json, application/xml, application/x-www-form-urlencoded, multipart/form-data

View File

@ -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 <nil>}", 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()