package response import ( "fmt" "io" "net/http" "github.com/gin-gonic/gin" ) func HandleResponses(c *gin.Context) { c.Next() if len(c.Errors) > 0 || c.Writer.Written() { return } v, ok := c.Get(responseKey{}) if !ok { return } res, ok := v.(Response) if !ok { return } writeResponse(c, res) } func HandleErrors(c *gin.Context) { c.Next() if len(c.Errors) == 0 || c.Writer.Written() { return } var ( err ErrorResponse ok bool ) for _, e := range c.Errors { if err, ok = GetError(e); ok { break } } if !ok { return } status, ok := err.GetStatus() if !ok { status = http.StatusInternalServerError } if h, ok := err.GetHTML(); ok { c.Status(status) c.Header("Content-Type", "text/html") c.Writer.Write(h) return } msg, ok := err.GetMsg() if !ok { msg = err.Error() } c.String(status, msg) } func writeResponse(c *gin.Context, res Response) { w := c.Writer // w.Header() must be set before ResponseWriter.WriteHeader is called // or redirect is attempted hdrs := w.Header() for _, ck := range res.getCookies() { hdrs.Add("Set-Cookie", ck.String()) } if ct, ok := res.getContentType(); ok { hdrs.Add("Content-Type", ct) } if code, to, ok := res.GetRedirect(); ok { http.Redirect(w, c.Request, to, code.Int()) return } // the body is written after the status header, but it's read here // first, because if an error is incurred, an error status header will // need to be written. body, bodySet, err := res.getBody() if err != nil { http.Error( w, fmt.Sprintf("Failed to construct response body: %v", err.Error()), http.StatusInternalServerError, ) return } if status, ok := res.GetStatus(); ok { w.WriteHeader(status) } if bodySet { // will automatically set the status header, // if w.WriteHeader wasn't already called io.Copy(w, body) } } func GetStatusFromError(err error) int { status := http.StatusInternalServerError if e, ok := GetError(err); ok { if status, ok = e.GetStatus(); !ok { status = http.StatusInternalServerError } } return status }