From 8837d19ce28e2d9d69177f826291e7fcc836f347 Mon Sep 17 00:00:00 2001 From: jqualls Date: Fri, 11 Nov 2022 19:26:59 -0700 Subject: [PATCH 1/3] feat: adds cookie binding --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ binding/binding.go | 1 + binding/cookie.go | 22 ++++++++++++++++++++++ context.go | 5 +++++ context_test.go | 21 +++++++++++++++++++++ 5 files changed, 89 insertions(+) create mode 100644 binding/cookie.go diff --git a/README.md b/README.md index 48a0a13cdb..77be09cdd1 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ Gin is a web framework written in Go (Golang). It features a martini-like API wi - [Bind Query String or Post Data](#bind-query-string-or-post-data) - [Bind Uri](#bind-uri) - [Bind Header](#bind-header) + - [Bind Cookie](#bind-cookie) - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [XML, JSON, YAML and ProtoBuf rendering](#xml-json-yaml-and-protobuf-rendering) @@ -1024,6 +1025,45 @@ func main() { } ``` +### Bind Cookie + +```go +package main + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +type testCookie struct { + Rate int `cookie:"Rate"` + Domain string `cookie:"Domain"` +} + +func main() { + r := gin.Default() + r.GET("/", func(c *gin.Context) { + h := testCookie{} + + if err := c.ShouldBindCookie(&h); err != nil { + c.JSON(http.StatusOK, err) + } + + fmt.Printf("%#v\n", h) + c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + }) + + r.Run() + +// client +// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ +// output +// {"Domain":"music","Rate":300} +} +``` + ### Bind HTML checkboxes See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092) diff --git a/binding/binding.go b/binding/binding.go index a58924ed3e..3846d170aa 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -85,6 +85,7 @@ var ( Uri = uriBinding{} Header = headerBinding{} TOML = tomlBinding{} + Cookie = cookieBinding{} ) // Default returns the appropriate Binding instance based on the HTTP method diff --git a/binding/cookie.go b/binding/cookie.go new file mode 100644 index 0000000000..d82c36c945 --- /dev/null +++ b/binding/cookie.go @@ -0,0 +1,22 @@ +// Copyright 2017 Manu Martinez-Almeida. All rights reserved. +// Use of this source code is governed by a MIT style +// license that can be found in the LICENSE file. + +package binding + +import "net/http" + +type cookieBinding struct{} + +func (cookieBinding) Name() string { + return "cookie" +} + +func (c cookieBinding) Bind(req *http.Request, obj any) error { + cookies := make(map[string][]string, len(req.Cookies())) + for _, cookie := range req.Cookies() { + cookies[cookie.Name] = append(cookies[cookie.Name], cookie.Value) + } + + return mapFormByTag(obj, cookies, c.Name()) +} diff --git a/context.go b/context.go index ac9db17e1c..6133168e72 100644 --- a/context.go +++ b/context.go @@ -725,6 +725,11 @@ func (c *Context) ShouldBindHeader(obj any) error { return c.ShouldBindWith(obj, binding.Header) } +// ShouldBindCookie is a shortcut for c.ShouldBindWith(obj, binding.Cookie). +func (c *Context) ShouldBindCookie(obj any) error { + return c.ShouldBindWith(obj, binding.Cookie) +} + // ShouldBindUri binds the passed struct pointer using the specified binding engine. func (c *Context) ShouldBindUri(obj any) error { m := make(map[string][]string) diff --git a/context_test.go b/context_test.go index 85e0a6161e..9f8794be1e 100644 --- a/context_test.go +++ b/context_test.go @@ -1796,6 +1796,27 @@ func TestContextShouldBindHeader(t *testing.T) { assert.Equal(t, 0, w.Body.Len()) } +func TestContextShouldBindCookie(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + + c.Request, _ = http.NewRequest("POST", "/", nil) + c.Request.AddCookie(&http.Cookie{Name: "rate", Value: "8000"}) + c.Request.AddCookie(&http.Cookie{Name: "domain", Value: "music"}) + c.Request.AddCookie(&http.Cookie{Name: "limit", Value: "1000"}) + + var testCookie struct { + Rate int `cookie:"rate"` + Domain string `cookie:"domain"` + Limit int `cookie:"limit"` + } + + assert.NoError(t, c.ShouldBindCookie(&testCookie)) + assert.Equal(t, 8000, testCookie.Rate) + assert.Equal(t, "music", testCookie.Domain) + assert.Equal(t, 1000, testCookie.Limit) +} + func TestContextShouldBindWithQuery(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) From 48c56178dba001888556034d655f4b9409526c68 Mon Sep 17 00:00:00 2001 From: jqualls Date: Sun, 12 Feb 2023 10:52:53 -0700 Subject: [PATCH 2/3] chore: ignore .idea files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index bdd50c95cf..cf574e0a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ count.out test profile.out tmp.out +.idea \ No newline at end of file From 582a8642923d053308e7d685151ce691b60e833c Mon Sep 17 00:00:00 2001 From: jqualls Date: Sun, 12 Feb 2023 10:56:06 -0700 Subject: [PATCH 3/3] chore: add bind cookie to documentation --- docs/doc.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/docs/doc.md b/docs/doc.md index 7cebab566e..d0efcc4083 100644 --- a/docs/doc.md +++ b/docs/doc.md @@ -28,6 +28,7 @@ - [Bind Query String or Post Data](#bind-query-string-or-post-data) - [Bind Uri](#bind-uri) - [Bind Header](#bind-header) + - [Bind Cookie](#bind-cookie) - [Bind HTML checkboxes](#bind-html-checkboxes) - [Multipart/Urlencoded binding](#multiparturlencoded-binding) - [XML, JSON, YAML, TOML and ProtoBuf rendering](#xml-json-yaml-toml-and-protobuf-rendering) @@ -900,6 +901,45 @@ func main() { } ``` +### Bind Cookie + +```go +package main + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +type testCookie struct { + Rate int `cookie:"Rate"` + Domain string `cookie:"Domain"` +} + +func main() { + r := gin.Default() + r.GET("/", func(c *gin.Context) { + h := testCookie{} + + if err := c.ShouldBindCookie(&h); err != nil { + c.JSON(http.StatusOK, err) + } + + fmt.Printf("%#v\n", h) + c.JSON(http.StatusOK, gin.H{"Rate": h.Rate, "Domain": h.Domain}) + }) + + r.Run() + +// client +// curl -H "rate:300" -H "domain:music" 127.0.0.1:8080/ +// output +// {"Domain":"music","Rate":300} +} +``` + ### Bind HTML checkboxes See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)