package response import ( "errors" "fmt" "io" "net/http" "github.com/gin-gonic/gin" "ruben/inventory2/consts" ) 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 { var ( err error status int statusFound bool ) for _, e := range c.Errors { err = errors.Join(err, e) if !statusFound { status, statusFound = mapErrorConstantsToStatus(e) } } if !statusFound { status = http.StatusInternalServerError } c.String(status, err.Error()) 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 _, hdr := range res.getHeaders() { hdrs.Add(hdr[0], hdr[1]) } for _, ck := range res.getCookies() { hdrs.Add("Set-Cookie", ck.String()) } if ct, ok := res.getContentType(); ok { hdrs.Set("Content-Type", ct) } if code, to, ok := res.GetRedirect(); ok { c.Redirect(code.Int(), to) c.Abort() 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 } func mapErrorConstantsToStatus(err error) (int, bool) { for { switch err { case consts.ErrBadRequest: return http.StatusBadRequest, true case consts.ErrNotFound: return http.StatusNotFound, true case consts.ErrConflict: return http.StatusConflict, true default: werr, ok := err.(interface { Unwrap() error }) if !ok { return 0, false } err = werr.Unwrap() } } }