Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to check error of render HTML template or manually clear response body? #2835

Open
nightlord189 opened this issue Aug 20, 2021 · 1 comment

Comments

@nightlord189
Copy link

nightlord189 commented Aug 20, 2021

I use server app writen on Gin for rendering pdf documents with wkhtmltopdf. wkhtmltopdf receives url with html to transforming it to pdf.

In case if i did some error in template or model (for example, template has variable SomeVar and i passed model without this field) gin's method c.HTML returns 500 and body filled with html (after recover). wkhtmltopdf doesn't check only status, if it see body - it works with it (although it has errors).

But i want to catch those cases and return 500 status with empty body in case if rendering completed with errors.

Are there any way to do it? I think it could be three paths:

  1. Add middleware after endpoint. But i cannot clear response body in middleware. Writer.Write only adds bytes to already written body, not reset it.
  2. Render HTML separately (without auto-writing it to response buffer) and check error. But i didn't find any method for this.
  3. Use recover in handler. But i cannot clear already filled response body in recover func, Abort... funcs don't clear body too.
@nightlord189 nightlord189 changed the title Any way to check error of render HTML template or clear response body? Any way to check error of render HTML template or manually clear response body? Aug 20, 2021
@MKCG
Copy link

MKCG commented Nov 4, 2021

You can replace the gin.Context writer to delay the response using an in memory writer. The response body can later on be sent to the client once it has been completely generated.

This should work :

import(
	"bytes"
	"net/http"
        "github.com/gin-gonic/gin"
)


type InMemoryWriter struct {
	gin.ResponseWriter
	body *bytes.Buffer
}

func (writer *InMemoryWriter) Write(b []byte) (int, error) {
	return writer.body.Write(b)
}

func (writer *InMemoryWriter) WriteString(s string) (int, error) {
	return writer.body.WriteString(s)
}

func DelayedResponse(c *gin.Context, callback func()) {
	writer := &InMemoryWriter{
		body: bytes.NewBufferString(""),
		ResponseWriter: c.Writer,
	}

	c.Writer = writer

	callback()

	body := writer.body.String()

        // if everything is ok : send the body to the client
	writer.ResponseWriter.WriteString(body)
}

func registerActions(engine *gin.Engine) {
    engine.GET("/pdf", func(c *gin.Context) {
    	DelayedResponse(c, func() {
    		c.HTML(http.StatusOK, "pdf.tmpl", gin.H{
		    	"foo": "bar"
		    })
    	})
    })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants