Merge tag 'tags/v1.11.0'
This commit is contained in:
+14
-5
@@ -7,6 +7,8 @@ package render
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"git.company.lan/gopkg/gin/internal/fs"
|
||||
)
|
||||
|
||||
// Delims represents a set of Left and Right delimiters for HTML template rendering.
|
||||
@@ -31,10 +33,12 @@ type HTMLProduction struct {
|
||||
|
||||
// HTMLDebug contains template delims and pattern and function with file list.
|
||||
type HTMLDebug struct {
|
||||
Files []string
|
||||
Glob string
|
||||
Delims Delims
|
||||
FuncMap template.FuncMap
|
||||
Files []string
|
||||
Glob string
|
||||
FileSystem http.FileSystem
|
||||
Patterns []string
|
||||
Delims Delims
|
||||
FuncMap template.FuncMap
|
||||
}
|
||||
|
||||
// HTML contains template reference and its name with given interface object.
|
||||
@@ -63,6 +67,7 @@ func (r HTMLDebug) Instance(name string, data any) Render {
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (r HTMLDebug) loadTemplate() *template.Template {
|
||||
if r.FuncMap == nil {
|
||||
r.FuncMap = template.FuncMap{}
|
||||
@@ -73,7 +78,11 @@ func (r HTMLDebug) loadTemplate() *template.Template {
|
||||
if r.Glob != "" {
|
||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseGlob(r.Glob))
|
||||
}
|
||||
panic("the HTML debug render was created without files or glob pattern")
|
||||
if r.FileSystem != nil && len(r.Patterns) > 0 {
|
||||
return template.Must(template.New("").Delims(r.Delims.Left, r.Delims.Right).Funcs(r.FuncMap).ParseFS(
|
||||
fs.FileSystem{FileSystem: r.FileSystem}, r.Patterns...))
|
||||
}
|
||||
panic("the HTML debug render was created without files or glob pattern or file system with patterns")
|
||||
}
|
||||
|
||||
// Render (HTML) executes template and writes its result with custom ContentType for response.
|
||||
|
||||
+16
-12
@@ -9,9 +9,10 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"unicode"
|
||||
|
||||
"git.company.lan/gopkg/gin/codec/json"
|
||||
"git.company.lan/gopkg/gin/internal/bytesconv"
|
||||
"git.company.lan/gopkg/gin/internal/json"
|
||||
)
|
||||
|
||||
// JSON contains the given interface object.
|
||||
@@ -65,7 +66,7 @@ func (r JSON) WriteContentType(w http.ResponseWriter) {
|
||||
// WriteJSON marshals the given interface object and writes it with custom ContentType.
|
||||
func WriteJSON(w http.ResponseWriter, obj any) error {
|
||||
writeContentType(w, jsonContentType)
|
||||
jsonBytes, err := json.Marshal(obj)
|
||||
jsonBytes, err := json.API.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -76,7 +77,7 @@ func WriteJSON(w http.ResponseWriter, obj any) error {
|
||||
// Render (IndentedJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r IndentedJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
jsonBytes, err := json.MarshalIndent(r.Data, "", " ")
|
||||
jsonBytes, err := json.API.MarshalIndent(r.Data, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -92,7 +93,7 @@ func (r IndentedJSON) WriteContentType(w http.ResponseWriter) {
|
||||
// Render (SecureJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r SecureJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
jsonBytes, err := json.Marshal(r.Data)
|
||||
jsonBytes, err := json.API.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -115,7 +116,7 @@ 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.Marshal(r.Data)
|
||||
ret, err := json.API.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -151,20 +152,23 @@ func (r JsonpJSON) WriteContentType(w http.ResponseWriter) {
|
||||
}
|
||||
|
||||
// Render (AsciiJSON) marshals the given interface object and writes it with custom ContentType.
|
||||
func (r AsciiJSON) Render(w http.ResponseWriter) (err error) {
|
||||
func (r AsciiJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
ret, err := json.Marshal(r.Data)
|
||||
ret, err := json.API.Marshal(r.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
escapeBuf := make([]byte, 0, 6) // Preallocate 6 bytes for Unicode escape sequences
|
||||
|
||||
for _, r := range bytesconv.BytesToString(ret) {
|
||||
cvt := string(r)
|
||||
if r >= 128 {
|
||||
cvt = fmt.Sprintf("\\u%04x", int64(r))
|
||||
if r > unicode.MaxASCII {
|
||||
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r) // Reuse escapeBuf
|
||||
buffer.Write(escapeBuf)
|
||||
} else {
|
||||
buffer.WriteByte(byte(r))
|
||||
}
|
||||
buffer.WriteString(cvt)
|
||||
}
|
||||
|
||||
_, err = w.Write(buffer.Bytes())
|
||||
@@ -179,7 +183,7 @@ func (r AsciiJSON) WriteContentType(w http.ResponseWriter) {
|
||||
// Render (PureJSON) writes custom ContentType and encodes the given interface object.
|
||||
func (r PureJSON) Render(w http.ResponseWriter) error {
|
||||
r.WriteContentType(w)
|
||||
encoder := json.NewEncoder(w)
|
||||
encoder := json.API.NewEncoder(w)
|
||||
encoder.SetEscapeHTML(false)
|
||||
return encoder.Encode(r.Data)
|
||||
}
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ func (r Reader) Render(w http.ResponseWriter) (err error) {
|
||||
}
|
||||
r.Headers["Content-Length"] = strconv.FormatInt(r.ContentLength, 10)
|
||||
}
|
||||
r.writeHeaders(w, r.Headers)
|
||||
r.writeHeaders(w)
|
||||
_, err = io.Copy(w, r.Reader)
|
||||
return
|
||||
}
|
||||
@@ -37,10 +37,10 @@ func (r Reader) WriteContentType(w http.ResponseWriter) {
|
||||
writeContentType(w, []string{r.ContentType})
|
||||
}
|
||||
|
||||
// writeHeaders writes custom Header.
|
||||
func (r Reader) writeHeaders(w http.ResponseWriter, headers map[string]string) {
|
||||
// writeHeaders writes headers from r.Headers into response.
|
||||
func (r Reader) writeHeaders(w http.ResponseWriter) {
|
||||
header := w.Header()
|
||||
for k, v := range headers {
|
||||
for k, v := range r.Headers {
|
||||
if header.Get(k) == "" {
|
||||
header.Set(k, v)
|
||||
}
|
||||
|
||||
+57
-23
@@ -15,7 +15,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.company.lan/gopkg/gin/internal/json"
|
||||
"git.company.lan/gopkg/gin/codec/json"
|
||||
testdata "git.company.lan/gopkg/gin/testdata/protoexample"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -38,7 +38,7 @@ func TestRenderJSON(t *testing.T) {
|
||||
err := (JSON{data}).Render(w)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
||||
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func TestRenderIndentedJSON(t *testing.T) {
|
||||
err := (IndentedJSON{data}).Render(w)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
|
||||
assert.JSONEq(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}", w.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func TestRenderSecureJSON(t *testing.T) {
|
||||
err1 := (SecureJSON{"while(1);", data}).Render(w1)
|
||||
|
||||
require.NoError(t, err1)
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", w1.Body.String())
|
||||
assert.JSONEq(t, "{\"foo\":\"bar\"}", w1.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w1.Header().Get("Content-Type"))
|
||||
|
||||
w2 := httptest.NewRecorder()
|
||||
@@ -173,7 +173,7 @@ func TestRenderJsonpJSONError(t *testing.T) {
|
||||
err = jsonpJSON.Render(ew)
|
||||
assert.Equal(t, `write "`+`(`+`" error`, err.Error())
|
||||
|
||||
data, _ := json.Marshal(jsonpJSON.Data) // error was returned while writing data
|
||||
data, _ := json.API.Marshal(jsonpJSON.Data) // error was returned while writing data
|
||||
ew.bufString = string(data)
|
||||
err = jsonpJSON.Render(ew)
|
||||
assert.Equal(t, `write "`+string(data)+`" error`, err.Error())
|
||||
@@ -194,7 +194,7 @@ func TestRenderJsonpJSONError2(t *testing.T) {
|
||||
e := (JsonpJSON{"", data}).Render(w)
|
||||
require.NoError(t, e)
|
||||
|
||||
assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String())
|
||||
assert.JSONEq(t, "{\"foo\":\"bar\"}", w.Body.String())
|
||||
assert.Equal(t, "application/javascript; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ func TestRenderAsciiJSON(t *testing.T) {
|
||||
err := (AsciiJSON{data1}).Render(w1)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
|
||||
assert.JSONEq(t, "{\"lang\":\"GO\\u8bed\\u8a00\",\"tag\":\"\\u003cbr\\u003e\"}", w1.Body.String())
|
||||
assert.Equal(t, "application/json", w1.Header().Get("Content-Type"))
|
||||
|
||||
w2 := httptest.NewRecorder()
|
||||
@@ -244,7 +244,7 @@ func TestRenderPureJSON(t *testing.T) {
|
||||
}
|
||||
err := (PureJSON{data}).Render(w)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
||||
assert.JSONEq(t, "{\"foo\":\"bar\",\"html\":\"<b>\"}\n", w.Body.String())
|
||||
assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
@@ -285,7 +285,14 @@ b:
|
||||
|
||||
err := (YAML{data}).Render(w)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "|4-\n a : Easy!\n b:\n \tc: 2\n \td: [3, 4]\n \t\n", w.Body.String())
|
||||
|
||||
// With github.com/goccy/go-yaml, the output format is different from gopkg.in/yaml.v3
|
||||
// We're checking that the output contains the expected data, not the exact formatting
|
||||
output := w.Body.String()
|
||||
assert.Contains(t, output, "a : Easy!")
|
||||
assert.Contains(t, output, "b:")
|
||||
assert.Contains(t, output, "c: 2")
|
||||
assert.Contains(t, output, "d: [3, 4]")
|
||||
assert.Equal(t, "application/yaml; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
@@ -369,7 +376,7 @@ func TestRenderXML(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRenderRedirect(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "/test-redirect", nil)
|
||||
req, err := http.NewRequest(http.MethodGet, "/test-redirect", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
data1 := Redirect{
|
||||
@@ -489,10 +496,12 @@ func TestRenderHTMLTemplateEmptyName(t *testing.T) {
|
||||
func TestRenderHTMLDebugFiles(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
htmlRender := HTMLDebug{
|
||||
Files: []string{"../testdata/template/hello.tmpl"},
|
||||
Glob: "",
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
Files: []string{"../testdata/template/hello.tmpl"},
|
||||
Glob: "",
|
||||
FileSystem: nil,
|
||||
Patterns: nil,
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]any{
|
||||
"name": "thinkerou",
|
||||
@@ -508,10 +517,33 @@ func TestRenderHTMLDebugFiles(t *testing.T) {
|
||||
func TestRenderHTMLDebugGlob(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
htmlRender := HTMLDebug{
|
||||
Files: nil,
|
||||
Glob: "../testdata/template/hello*",
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
Files: nil,
|
||||
Glob: "../testdata/template/hello*",
|
||||
FileSystem: nil,
|
||||
Patterns: nil,
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]any{
|
||||
"name": "thinkerou",
|
||||
})
|
||||
|
||||
err := instance.Render(w)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "<h1>Hello thinkerou</h1>", w.Body.String())
|
||||
assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type"))
|
||||
}
|
||||
|
||||
func TestRenderHTMLDebugFS(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
htmlRender := HTMLDebug{
|
||||
Files: nil,
|
||||
Glob: "",
|
||||
FileSystem: http.Dir("../testdata/template"),
|
||||
Patterns: []string{"hello.tmpl"},
|
||||
Delims: Delims{Left: "{[{", Right: "}]}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
instance := htmlRender.Instance("hello.tmpl", map[string]any{
|
||||
"name": "thinkerou",
|
||||
@@ -526,10 +558,12 @@ func TestRenderHTMLDebugGlob(t *testing.T) {
|
||||
|
||||
func TestRenderHTMLDebugPanics(t *testing.T) {
|
||||
htmlRender := HTMLDebug{
|
||||
Files: nil,
|
||||
Glob: "",
|
||||
Delims: Delims{"{{", "}}"},
|
||||
FuncMap: nil,
|
||||
Files: nil,
|
||||
Glob: "",
|
||||
FileSystem: nil,
|
||||
Patterns: nil,
|
||||
Delims: Delims{"{{", "}}"},
|
||||
FuncMap: nil,
|
||||
}
|
||||
assert.Panics(t, func() { htmlRender.Instance("", nil) })
|
||||
}
|
||||
@@ -581,7 +615,7 @@ func TestRenderReaderNoContentLength(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRenderWriteError(t *testing.T) {
|
||||
data := []interface{}{"value1", "value2"}
|
||||
data := []any{"value1", "value2"}
|
||||
prefix := "my-prefix:"
|
||||
r := SecureJSON{Data: data, Prefix: prefix}
|
||||
ew := &errorWriter{
|
||||
|
||||
+2
-2
@@ -15,7 +15,7 @@ type TOML struct {
|
||||
Data any
|
||||
}
|
||||
|
||||
var TOMLContentType = []string{"application/toml; charset=utf-8"}
|
||||
var tomlContentType = []string{"application/toml; charset=utf-8"}
|
||||
|
||||
// Render (TOML) marshals the given interface object and writes data with custom ContentType.
|
||||
func (r TOML) Render(w http.ResponseWriter) error {
|
||||
@@ -32,5 +32,5 @@ func (r TOML) Render(w http.ResponseWriter) error {
|
||||
|
||||
// WriteContentType (TOML) writes TOML ContentType for response.
|
||||
func (r TOML) WriteContentType(w http.ResponseWriter) {
|
||||
writeContentType(w, TOMLContentType)
|
||||
writeContentType(w, tomlContentType)
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ package render
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/goccy/go-yaml"
|
||||
)
|
||||
|
||||
// YAML contains the given interface object.
|
||||
|
||||
Reference in New Issue
Block a user