From e7564a55ac53170ea7adbcc2677c44c23648d311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AE=97=E8=BE=89?= <1050527083@qq.com> Date: Sat, 16 Nov 2024 00:37:31 +0800 Subject: [PATCH 1/6] Change the annotation names in line 90 of context.go that are different from the attributes to be consistent, and change the first letter to lowercase --- context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context.go b/context.go index c724daf344..d978a55ec3 100644 --- a/context.go +++ b/context.go @@ -87,7 +87,7 @@ type Context struct { // or PUT body parameters. formCache url.Values - // SameSite allows a server to define a cookie attribute making it impossible for + // sameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite } From 75eefd484addd3995674e48fe59a89bbf5f815d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AE=97=E8=BE=89?= <1050527083@qq.com> Date: Sat, 16 Nov 2024 02:20:21 +0800 Subject: [PATCH 2/6] Added JSON initialization function --- render/json.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/render/json.go b/render/json.go index fc8dea453f..80e0a9eb7c 100644 --- a/render/json.go +++ b/render/json.go @@ -19,6 +19,10 @@ type JSON struct { Data any } +func NewJSON(data any) Render { + return &JSON{Data: data} +} + // IndentedJSON contains the given interface object. type IndentedJSON struct { Data any From ea103661a9319378bfcf4a847469c5c204e1fd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AE=97=E8=BE=89?= <1050527083@qq.com> Date: Sat, 16 Nov 2024 02:30:10 +0800 Subject: [PATCH 3/6] Fix issue #4094:By using structure encapsulation parameters to achieve the purpose of user-defined JSON rendering, while not changing the functions implemented by the original code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 朱宗辉 <1050527083@qq.com> --- context.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/context.go b/context.go index d978a55ec3..70ac8005aa 100644 --- a/context.go +++ b/context.go @@ -52,6 +52,26 @@ const ContextRequestKey ContextKeyType = 0 // abortIndex represents a typical value used in abort functions. const abortIndex int8 = math.MaxInt8 >> 1 +// defaultJsonRenderName, default JSON render name +const defaultJsonRenderName string = "render.JSON" + +// jsonRenderRegistry, storing JSON render types and their corresponding creation factory functions +var jsonRenderRegistry = make(map[string]func(data any) render.Render) + +// RegisterJsonRender allows users to register JSON render factory functions +func (c *Context) RegisterJsonRender(name string, jsonRender func(data any) render.Render) { + jsonRenderRegistry[name] = jsonRender +} + +// createInstance, Create a JSON render instance through a factory function +func createInstance(name string, data any) render.Render { + jsonRender, exists := jsonRenderRegistry[name] + if !exists { + return jsonRenderRegistry[defaultJsonRenderName](data) + } + return jsonRender(data) +} + // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { @@ -90,6 +110,15 @@ type Context struct { // sameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite + + // jsonRenderName the name of the JSON render currently used. The default value is render.JSON + // this is used to get the JSON render type when executing the c.JSON function. + jsonRenderName string +} + +// SetJSONRender set the name of the current JSON render +func (c *Context) SetJSONRender(jsonRenderName string) { + c.jsonRenderName = jsonRenderName } /************************************/ @@ -109,6 +138,9 @@ func (c *Context) reset() { c.queryCache = nil c.formCache = nil c.sameSite = 0 + // registering a default JSON renderer + c.jsonRenderName = defaultJsonRenderName + c.RegisterJsonRender(defaultJsonRenderName, render.NewJSON) *c.params = (*c.params)[:0] *c.skippedNodes = (*c.skippedNodes)[:0] } @@ -1095,7 +1127,7 @@ func (c *Context) JSONP(code int, obj any) { // JSON serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". func (c *Context) JSON(code int, obj any) { - c.Render(code, render.JSON{Data: obj}) + c.Render(code, createInstance(c.jsonRenderName, obj)) } // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. From 6144ce5af33a146aac32c3949ddde845e8136703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AE=97=E8=BE=89?= <1050527083@qq.com> Date: Sat, 16 Nov 2024 02:34:02 +0800 Subject: [PATCH 4/6] Testing custom JSON render --- context_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/context_test.go b/context_test.go index 5b63a647c9..7ddc407907 100644 --- a/context_test.go +++ b/context_test.go @@ -9,6 +9,7 @@ import ( "context" "errors" "fmt" + "github.com/gin-gonic/gin/render" "html/template" "io" "io/fs" @@ -3123,3 +3124,32 @@ func TestContextNext(t *testing.T) { assert.True(t, exists) assert.Equal(t, "value3", value) } + +// MyJSON customizing JSON rendering +type MyJSON struct { + Data any + render.JSON +} + +// Render rewrite the Render function +func (r MyJSON) Render(w http.ResponseWriter) error { + _, err := w.Write([]byte("test")) + return err +} + +func NewMyJSON(data any) render.Render { + return &MyJSON{Data: data} +} + +// TestCustomJSONRender the test uses a custom JSON render. +// The final result is that the user can customize the JSON render without affecting the original function. +func TestCustomJSONRender(t *testing.T) { + w := httptest.NewRecorder() + c, _ := CreateTestContext(w) + c.RegisterJsonRender("MyTestJSON", NewMyJSON) + c.SetJSONRender("MyTestJSON") + + c.JSON(http.StatusCreated, H{"foo": "bar", "html": ""}) + + t.Log(w.Body.String()) +} From 87ce468c7850a0ffc0a6a629eca6ae086816c2fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E5=AE=97=E8=BE=89?= <1050527083@qq.com> Date: Sun, 17 Nov 2024 16:09:14 +0800 Subject: [PATCH 5/6] Optimize JSON.render renderer code --- context.go | 37 ++++++++----------------------------- context_test.go | 3 +-- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/context.go b/context.go index 70ac8005aa..209ca2e19d 100644 --- a/context.go +++ b/context.go @@ -52,26 +52,6 @@ const ContextRequestKey ContextKeyType = 0 // abortIndex represents a typical value used in abort functions. const abortIndex int8 = math.MaxInt8 >> 1 -// defaultJsonRenderName, default JSON render name -const defaultJsonRenderName string = "render.JSON" - -// jsonRenderRegistry, storing JSON render types and their corresponding creation factory functions -var jsonRenderRegistry = make(map[string]func(data any) render.Render) - -// RegisterJsonRender allows users to register JSON render factory functions -func (c *Context) RegisterJsonRender(name string, jsonRender func(data any) render.Render) { - jsonRenderRegistry[name] = jsonRender -} - -// createInstance, Create a JSON render instance through a factory function -func createInstance(name string, data any) render.Render { - jsonRender, exists := jsonRenderRegistry[name] - if !exists { - return jsonRenderRegistry[defaultJsonRenderName](data) - } - return jsonRender(data) -} - // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { @@ -111,14 +91,14 @@ type Context struct { // the browser to send this cookie along with cross-site requests. sameSite http.SameSite - // jsonRenderName the name of the JSON render currently used. The default value is render.JSON - // this is used to get the JSON render type when executing the c.JSON function. - jsonRenderName string + // jsonRender the JSON renderer currently used. The default value is render.JSON, + // which is used to get the JSON rendering type when executing the c.JSON function. + jsonRender func(data any) render.Render } -// SetJSONRender set the name of the current JSON render -func (c *Context) SetJSONRender(jsonRenderName string) { - c.jsonRenderName = jsonRenderName +// SetJSONRender set the current JSON render +func (c *Context) SetJSONRender(jsonRender func(data any) render.Render) { + c.jsonRender = jsonRender } /************************************/ @@ -139,8 +119,7 @@ func (c *Context) reset() { c.formCache = nil c.sameSite = 0 // registering a default JSON renderer - c.jsonRenderName = defaultJsonRenderName - c.RegisterJsonRender(defaultJsonRenderName, render.NewJSON) + c.jsonRender = render.NewJSON *c.params = (*c.params)[:0] *c.skippedNodes = (*c.skippedNodes)[:0] } @@ -1127,7 +1106,7 @@ func (c *Context) JSONP(code int, obj any) { // JSON serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". func (c *Context) JSON(code int, obj any) { - c.Render(code, createInstance(c.jsonRenderName, obj)) + c.Render(code, c.jsonRender(obj)) } // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. diff --git a/context_test.go b/context_test.go index 7ddc407907..64bf5d99a7 100644 --- a/context_test.go +++ b/context_test.go @@ -3146,8 +3146,7 @@ func NewMyJSON(data any) render.Render { func TestCustomJSONRender(t *testing.T) { w := httptest.NewRecorder() c, _ := CreateTestContext(w) - c.RegisterJsonRender("MyTestJSON", NewMyJSON) - c.SetJSONRender("MyTestJSON") + c.SetJSONRender(NewMyJSON) c.JSON(http.StatusCreated, H{"foo": "bar", "html": ""}) From fc1a19e6cc85756d9e553d999b66043341f05a86 Mon Sep 17 00:00:00 2001 From: ChenPuChu <65607018+ChenPuChu@users.noreply.github.com> Date: Thu, 21 Nov 2024 10:12:02 +0000 Subject: [PATCH 6/6] Fix golangci-lint issues --- context_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context_test.go b/context_test.go index 64bf5d99a7..6adddc4724 100644 --- a/context_test.go +++ b/context_test.go @@ -167,7 +167,7 @@ func TestSaveUploadedFileWithPermission(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost,, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -188,7 +188,7 @@ func TestSaveUploadedFileWithPermissionFailed(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err)