From 2e41772d61c43868ca89a50fa3d79e7bc5b4cec5 Mon Sep 17 00:00:00 2001 From: iliya Date: Sun, 15 Jan 2023 17:17:18 +0330 Subject: [PATCH 1/2] feature: write query bool parser for query parameters. --- context.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ context_test.go | 31 ++++++++++++++++++++++++++- docs/doc.md | 5 +++-- 3 files changed, 89 insertions(+), 3 deletions(-) diff --git a/context.go b/context.go index b1352b9b..81555210 100644 --- a/context.go +++ b/context.go @@ -15,6 +15,7 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "sync" "time" @@ -486,6 +487,61 @@ func (c *Context) GetQueryMap(key string) (map[string]string, bool) { return c.get(c.queryCache, key) } +// QueryBool returns a value of boolean type for given query key. +// HINT: if given key is not exist or not valid, it returns false. +// +// GET /?name=alex&force=true&delivery= +// c.QueryBool("force") == true +// c.QueryBool("name") == false +// c.QueryBool("delivery") == false +func (c *Context) QueryBool(key string) bool { + + value, _ := c.GetQuery(key) + boolValue, _ := strconv.ParseBool(value) + + return boolValue +} + +// GetQueryBoolDefault returns the keyed boolean url query value if it exists and valid, +// otherwise it returns the specified defaultValue string. +// +// GET /?name=alex&force=true&delivery= +// c.GetQueryBoolDefault("force", false) == true +// c.GetQueryBoolDefault("name", false) == false +// c.GetQueryBoolDefault("delivery", false) == false +func (c *Context) GetQueryBoolDefault(key string, defaultValue bool) bool { + + if value, ok := c.GetQuery(key); ok { + boolValue, err := strconv.ParseBool(value) + if err != nil { + return defaultValue + } + return boolValue + } + + return defaultValue +} + +// GetQueryBool is like QueryBool(), it returns the keyed url query value but in boolean, +// if key exists and valid it returns (value, true) +// but if key is not exist or not valid, it returns (false, false) +// +// GET /?name=alex&force=false&delivery= +// GetQueryBool("force") == false, true +// GetQueryBool("name") == false, false +// GetQueryBool("delivery") == false, false +func (c *Context) GetQueryBool(key string) (bool, bool) { + if value, ok := c.GetQuery(key); ok { + boolValue, err := strconv.ParseBool(value) + if err != nil { + return false, false + } + return boolValue, true + } + + return false, false +} + // PostForm returns the specified key from a POST urlencoded form or multipart form // when it exists, otherwise it returns an empty string `("")`. func (c *Context) PostForm(key string) (value string) { diff --git a/context_test.go b/context_test.go index 85e0a616..58a31fdb 100644 --- a/context_test.go +++ b/context_test.go @@ -373,7 +373,7 @@ func TestContextHandler(t *testing.T) { func TestContextQuery(t *testing.T) { c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil) + c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&read=true&id=", nil) value, ok := c.GetQuery("foo") assert.True(t, ok) @@ -404,6 +404,35 @@ func TestContextQuery(t *testing.T) { assert.False(t, ok) assert.Empty(t, value) assert.Empty(t, c.PostForm("foo")) + + valueBool := c.QueryBool("read") + assert.True(t, valueBool) + assert.Equal(t, true, valueBool) + + valueBool = c.QueryBool("id") + assert.False(t, valueBool) + assert.Equal(t, false, valueBool) + + valueBool, ok = c.GetQueryBool("read") + assert.True(t, ok) + assert.True(t, valueBool) + assert.Equal(t, true, valueBool) + assert.Equal(t, true, ok) + + valueBool, ok = c.GetQueryBool("id") + assert.False(t, ok) + assert.False(t, valueBool) + assert.Equal(t, valueBool, false) + assert.Equal(t, ok, false) + + valueBool = c.GetQueryBoolDefault("read", false) + assert.True(t, valueBool) + assert.Equal(t, true, valueBool) + + valueBool = c.GetQueryBoolDefault("id", true) + assert.True(t, valueBool) + assert.Equal(t, true, valueBool) + } func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { diff --git a/docs/doc.md b/docs/doc.md index 7cebab56..943007df 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -169,12 +169,13 @@ func main() { router := gin.Default() // Query string parameters are parsed using the existing underlying request object. - // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe + // The request responds to an url matching: /welcome?firstname=Jane&lastname=Doe&want_pizza=true router.GET("/welcome", func(c *gin.Context) { firstname := c.DefaultQuery("firstname", "Guest") lastname := c.Query("lastname") // shortcut for c.Request.URL.Query().Get("lastname") + wantPizza := c.QueryBool("want_pizza") - c.String(http.StatusOK, "Hello %s %s", firstname, lastname) + c.String(http.StatusOK, "%s %s want a pizza? %v", firstname, lastname, wantPizza) }) router.Run(":8080") } From 9ce3d65a1112d8fed1376de3facf62289a6770dd Mon Sep 17 00:00:00 2001 From: iliya Date: Sat, 4 Feb 2023 12:28:23 +0330 Subject: [PATCH 2/2] make default value in GetQueryBool true, return error in QueryBool --- context.go | 16 ++++++++-------- context_test.go | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/context.go b/context.go index 81555210..d1a0dab7 100644 --- a/context.go +++ b/context.go @@ -494,12 +494,12 @@ func (c *Context) GetQueryMap(key string) (map[string]string, bool) { // c.QueryBool("force") == true // c.QueryBool("name") == false // c.QueryBool("delivery") == false -func (c *Context) QueryBool(key string) bool { +func (c *Context) QueryBool(key string) (bool, error) { value, _ := c.GetQuery(key) - boolValue, _ := strconv.ParseBool(value) + boolValue, err := strconv.ParseBool(value) - return boolValue + return boolValue, err } // GetQueryBoolDefault returns the keyed boolean url query value if it exists and valid, @@ -524,22 +524,22 @@ func (c *Context) GetQueryBoolDefault(key string, defaultValue bool) bool { // GetQueryBool is like QueryBool(), it returns the keyed url query value but in boolean, // if key exists and valid it returns (value, true) -// but if key is not exist or not valid, it returns (false, false) +// but if key is not exist or not valid, it returns (true, false) // // GET /?name=alex&force=false&delivery= // GetQueryBool("force") == false, true -// GetQueryBool("name") == false, false -// GetQueryBool("delivery") == false, false +// GetQueryBool("name") == true, false +// GetQueryBool("delivery") == true, false func (c *Context) GetQueryBool(key string) (bool, bool) { if value, ok := c.GetQuery(key); ok { boolValue, err := strconv.ParseBool(value) if err != nil { - return false, false + return true, false } return boolValue, true } - return false, false + return true, false } // PostForm returns the specified key from a POST urlencoded form or multipart form diff --git a/context_test.go b/context_test.go index 58a31fdb..1fab0782 100644 --- a/context_test.go +++ b/context_test.go @@ -405,11 +405,11 @@ func TestContextQuery(t *testing.T) { assert.Empty(t, value) assert.Empty(t, c.PostForm("foo")) - valueBool := c.QueryBool("read") + valueBool, _ := c.QueryBool("read") assert.True(t, valueBool) assert.Equal(t, true, valueBool) - valueBool = c.QueryBool("id") + valueBool, _ = c.QueryBool("id") assert.False(t, valueBool) assert.Equal(t, false, valueBool) @@ -421,8 +421,8 @@ func TestContextQuery(t *testing.T) { valueBool, ok = c.GetQueryBool("id") assert.False(t, ok) - assert.False(t, valueBool) - assert.Equal(t, valueBool, false) + assert.True(t, valueBool) + assert.Equal(t, valueBool, true) assert.Equal(t, ok, false) valueBool = c.GetQueryBoolDefault("read", false)