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