Skip to content

Commit

Permalink
πŸ”₯ Add function to override form decoder setting (#1100)
Browse files Browse the repository at this point in the history
* πŸ”₯ 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
  • Loading branch information
rockcreation7 committed Oct 1, 2021
1 parent 26c29e2 commit 35e38db
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 3 deletions.
41 changes: 38 additions & 3 deletions ctx.go
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
54 changes: 54 additions & 0 deletions ctx_test.go
Expand Up @@ -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()
Expand Down

0 comments on commit 35e38db

Please sign in to comment.