From b7afe5a6af9e2c083f0b331c19cac8aa87f72a03 Mon Sep 17 00:00:00 2001 From: 1911860538 Date: Sat, 8 Nov 2025 13:35:02 +0800 Subject: [PATCH 1/2] fix(render): improve JsonpJSON content type handling and simplify Context.JSONP --- context.go | 9 +++------ render/json.go | 4 +++- render/render_test.go | 23 ++++++++++++++++------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/context.go b/context.go index d5ef8b81..918e9330 100644 --- a/context.go +++ b/context.go @@ -1135,13 +1135,10 @@ func (c *Context) SecureJSON(code int, obj any) { // JSONP serializes the given struct as JSON into the response body. // It adds padding to response body to request data from a server residing in a different domain than the client. // It also sets the Content-Type as "application/javascript". +// +// When the callback parameter is empty, it behaves equivalently to Context.JSON. func (c *Context) JSONP(code int, obj any) { - callback := c.DefaultQuery("callback", "") - if callback == "" { - c.Render(code, render.JSON{Data: obj}) - return - } - c.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) + c.Render(code, render.JsonpJSON{Callback: c.Query("callback"), Data: obj}) } // JSON serializes the given struct as JSON into the response body. diff --git a/render/json.go b/render/json.go index 2f98676c..8f4b144b 100644 --- a/render/json.go +++ b/render/json.go @@ -115,17 +115,19 @@ func (r SecureJSON) WriteContentType(w http.ResponseWriter) { // Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType. func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { - r.WriteContentType(w) ret, err := json.API.Marshal(r.Data) if err != nil { return err } if r.Callback == "" { + writeContentType(w, jsonContentType) _, err = w.Write(ret) return err } + r.WriteContentType(w) + callback := template.JSEscapeString(r.Callback) if _, err = w.Write(bytesconv.StringToBytes(callback)); err != nil { return err diff --git a/render/render_test.go b/render/render_test.go index d9ae2067..180b0b0c 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -183,19 +183,28 @@ func TestRenderJsonpJSONError(t *testing.T) { assert.Equal(t, `write "`+`);`+`" error`, err.Error()) } -func TestRenderJsonpJSONError2(t *testing.T) { +func TestRenderJsonpJSONWithEmptyCallback(t *testing.T) { w := httptest.NewRecorder() data := map[string]any{ "foo": "bar", + "num": 42, + "nested": map[string]any{ + "key": "value", + }, } - (JsonpJSON{"", data}).WriteContentType(w) - assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type")) - e := (JsonpJSON{"", data}).Render(w) - require.NoError(t, e) + err := (JsonpJSON{Callback: "", Data: data}).Render(w) - assert.JSONEq(t, "{\"foo\":\"bar\"}", w.Body.String()) - assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type")) + require.NoError(t, err) + + // Verify Content-Type is set to jsonContentType when callback is empty + assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) + + renderData, err := json.API.Marshal(data) + require.NoError(t, err) + + // Verify body contains correct JSON data + assert.JSONEq(t, string(renderData), w.Body.String()) } func TestRenderJsonpJSONFail(t *testing.T) { From a14cf7c08dc37b7f9308248928aa3b16307f3c2f Mon Sep 17 00:00:00 2001 From: 1911860538 Date: Sat, 8 Nov 2025 22:06:23 +0800 Subject: [PATCH 2/2] refactor(render): use WriteJSON when JsonpJson.Callback is empty --- render/json.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/render/json.go b/render/json.go index 8f4b144b..ceeeca9c 100644 --- a/render/json.go +++ b/render/json.go @@ -115,19 +115,17 @@ func (r SecureJSON) WriteContentType(w http.ResponseWriter) { // Render (JsonpJSON) marshals the given interface object and writes it and its callback with custom ContentType. func (r JsonpJSON) Render(w http.ResponseWriter) (err error) { + if r.Callback == "" { + return WriteJSON(w, r.Data) + } + + r.WriteContentType(w) + ret, err := json.API.Marshal(r.Data) if err != nil { return err } - if r.Callback == "" { - writeContentType(w, jsonContentType) - _, err = w.Write(ret) - return err - } - - r.WriteContentType(w) - callback := template.JSEscapeString(r.Callback) if _, err = w.Write(bytesconv.StringToBytes(callback)); err != nil { return err