1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package controller
import (
"errors"
"net/http"
"sync"
"github.com/elnormous/contenttype"
"github.com/labstack/echo/v4"
)
type ContentTypeNegotiatingHandler struct {
Handlers map[string]echo.HandlerFunc
DefaultHandler echo.HandlerFunc
mediaTypes []contenttype.MediaType
once sync.Once
}
func errorIsNotAcceptable(err error) bool {
return errors.Is(err, contenttype.ErrNoAcceptableTypeFound) ||
errors.Is(err, contenttype.ErrNoAvailableTypeGiven)
}
func errorIsBadRequest(err error) bool {
return errors.Is(err, contenttype.ErrInvalidMediaType) ||
errors.Is(err, contenttype.ErrInvalidMediaRange) ||
errors.Is(err, contenttype.ErrInvalidParameter) ||
errors.Is(err, contenttype.ErrInvalidExtensionParameter) ||
errors.Is(err, contenttype.ErrInvalidWeight)
}
func (h *ContentTypeNegotiatingHandler) Handle(c echo.Context) error {
h.once.Do(func() {
h.mediaTypes = []contenttype.MediaType{}
for k, _ := range h.Handlers {
h.mediaTypes = append(h.mediaTypes, contenttype.NewMediaType(k))
}
})
handler := h.DefaultHandler
ct, _, err := contenttype.GetAcceptableMediaType(c.Request(), h.mediaTypes)
if err == nil {
handler = h.Handlers[ct.String()]
} else if errorIsNotAcceptable(err) {
return echo.NewHTTPError(http.StatusNotAcceptable)
} else if errorIsBadRequest(err) {
return echo.NewHTTPError(http.StatusBadRequest)
}
// If negotiation failed but it wasn't an error and there is no default
// handler then the request is still not acceptable
if handler == nil {
return echo.NewHTTPError(http.StatusNotAcceptable)
}
// Don't force each handler to do this itself to eliminate redundant code
c.Response().Header().Set("Content-Type", ct.String())
return handler(c)
}
|