168 lines
2.9 KiB
Go
168 lines
2.9 KiB
Go
package response
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"ruben/inventory2/internal/consts"
|
|
|
|
"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 {
|
|
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()
|
|
}
|
|
}
|
|
}
|