diff --git a/context.go b/context.go index 6fb2091b8..56cd54498 100644 --- a/context.go +++ b/context.go @@ -4,7 +4,6 @@ package echo import ( - "bytes" "encoding/xml" "errors" "fmt" @@ -414,11 +413,9 @@ func (c *Context) Render(code int, name string, data any) (err error) { if c.echo.Renderer == nil { return ErrRendererNotRegistered } - buf := new(bytes.Buffer) - if err = c.echo.Renderer.Render(c, buf, name, data); err != nil { - return - } - return c.HTMLBlob(code, buf.Bytes()) + return c.HTMLWrite(code, func(wr io.Writer) error { + return c.echo.Renderer.Render(c, wr, name, data) + }) } // HTML sends an HTTP response with status code. @@ -431,6 +428,10 @@ func (c *Context) HTMLBlob(code int, b []byte) (err error) { return c.Blob(code, MIMETextHTMLCharsetUTF8, b) } +func (c *Context) HTMLWrite(code int, w func(io.Writer) error) (err error) { + return c.BlobWrite(code, MIMETextHTMLCharsetUTF8, w) +} + // String sends a string response with status code. func (c *Context) String(code int, s string) (err error) { return c.Blob(code, MIMETextPlainCharsetUTF8, []byte(s)) @@ -535,6 +536,13 @@ func (c *Context) Blob(code int, contentType string, b []byte) (err error) { return } +func (c *Context) BlobWrite(code int, contentType string, w func(io.Writer) error) (err error) { + c.writeContentType(contentType) + c.response.WriteHeader(code) + err = w(c.response) + return +} + // Stream sends a streaming response with status code and content type. func (c *Context) Stream(code int, contentType string, r io.Reader) (err error) { c.writeContentType(contentType) diff --git a/context_test.go b/context_test.go index 1ac517cfc..0c9f49376 100644 --- a/context_test.go +++ b/context_test.go @@ -138,6 +138,25 @@ func TestContextRenderTemplate(t *testing.T) { } } +func BenchmarkContextRenderTemplate(b *testing.B) { + e := New() + req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(userJSON)) + + tmpl := &Template{ + templates: template.Must(template.New("hello").Parse("Hello, {{.}}!")), + } + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + c.Echo().Renderer = tmpl + _ = c.Render(http.StatusOK, "hello", "Jon Snow") + } +} + func TestContextRenderErrorsOnNoRenderer(t *testing.T) { e := New() req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(userJSON)) @@ -200,6 +219,23 @@ func TestContextHTMLBlob(t *testing.T) { } } +func TestContextHTMLWrite(t *testing.T) { + e := New() + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/", nil) + c := e.NewContext(req, rec) + + err := c.HTMLWrite(http.StatusOK, func(w io.Writer) error { + _, err := w.Write([]byte("Hi, Jon Snow")) + return err + }) + if assert.NoError(t, err) { + assert.Equal(t, http.StatusOK, rec.Code) + assert.Equal(t, MIMETextHTMLCharsetUTF8, rec.Header().Get(HeaderContentType)) + assert.Equal(t, "Hi, Jon Snow", rec.Body.String()) + } +} + func TestContextJSON(t *testing.T) { e := New() rec := httptest.NewRecorder()