aboutsummaryrefslogtreecommitdiff
path: root/echo/error_handler.go
diff options
context:
space:
mode:
Diffstat (limited to 'echo/error_handler.go')
-rw-r--r--echo/error_handler.go120
1 files changed, 90 insertions, 30 deletions
diff --git a/echo/error_handler.go b/echo/error_handler.go
index bfbcfb6..bb4102b 100644
--- a/echo/error_handler.go
+++ b/echo/error_handler.go
@@ -7,19 +7,94 @@ import (
7 "io/fs" 7 "io/fs"
8 "net/http" 8 "net/http"
9 9
10 "github.com/elnormous/contenttype"
10 "github.com/labstack/echo/v4" 11 "github.com/labstack/echo/v4"
11) 12)
12 13
13// Copied from echo and tweaked to make our errors nicer 14// TODO: This should allow plugging in other content types
15// TODO: This should also be refactored into something prettier
14func ErrorHandler(templates fs.FS, funcs template.FuncMap) func(error, echo.Context) { 16func ErrorHandler(templates fs.FS, funcs template.FuncMap) func(error, echo.Context) {
17 handleHtml := func(c echo.Context, he *echo.HTTPError) error {
18 t, err := template.New("").Funcs(funcs).ParseFS(
19 templates,
20 "404.tpl",
21 "40x.tpl",
22 "50x.tpl",
23 "header.tpl",
24 "footer.tpl",
25 )
26 if err != nil {
27 return err
28 }
29
30 path := "50x.tpl"
31 if he.Code == 404 {
32 path = "404.tpl"
33 } else if he.Code >= 400 && he.Code <= 499 {
34 path = "40x.tpl"
35 }
36
37 buf := bytes.Buffer{}
38 if err = t.ExecuteTemplate(&buf, path, nil); err != nil {
39 err = c.String(he.Code, fmt.Sprintf("%s", he.Message))
40 }
41
42 return c.HTMLBlob(he.Code, buf.Bytes())
43 }
44
45 handlePlain := func(c echo.Context, he *echo.HTTPError) error {
46 return c.String(he.Code, fmt.Sprintf("%s", he.Message))
47 }
48
49 handleJson := func(c echo.Context, he *echo.HTTPError) error {
50 code := he.Code
51 message := he.Message
52 if m, ok := he.Message.(string); ok {
53 if c.Echo().Debug {
54 message = echo.Map{"message": m, "error": he.Error()}
55 } else {
56 message = echo.Map{"message": m}
57 }
58 }
59
60 return c.JSON(code, message)
61 }
62
63 errorWhileErroring := func(c echo.Context, err interface{}) {
64 c.Echo().Logger.Error(err)
65 if c.Echo().Debug {
66 c.JSON(http.StatusInternalServerError, &echo.HTTPError{
67 Code: http.StatusInternalServerError,
68 Message: fmt.Sprintf("Error while processing error page. %w", err),
69 })
70 } else {
71 c.JSON(http.StatusInternalServerError, &echo.HTTPError{
72 Code: http.StatusInternalServerError,
73 Message: http.StatusText(http.StatusInternalServerError),
74 })
75 }
76 }
77
78 handlers := map[string]func(echo.Context, *echo.HTTPError) error{
79 "text/plain": handlePlain,
80 "text/html": handleHtml,
81 "text/json": handleJson,
82 "application/json": handleJson,
83 }
84
85 // This is hand maintained here because order is important for negotiation,
86 // especially in the case of */*
87 hIndex := []contenttype.MediaType{
88 contenttype.NewMediaType("text/json"),
89 contenttype.NewMediaType("application/json"),
90 contenttype.NewMediaType("text/plain"),
91 contenttype.NewMediaType("text/html"),
92 }
93
15 return func(err error, c echo.Context) { 94 return func(err error, c echo.Context) {
16 defer func() { 95 defer func() {
17 if r := recover(); r != nil { 96 if r := recover(); r != nil {
18 if c.Echo().Debug { 97 errorWhileErroring(c, r)
19 c.String(http.StatusInternalServerError, fmt.Sprintf("Error while processing error page. %s", r))
20 } else {
21 c.String(http.StatusInternalServerError, "Error while processing error page.")
22 }
23 } 98 }
24 }() 99 }()
25 100
@@ -37,41 +112,26 @@ func ErrorHandler(templates fs.FS, funcs template.FuncMap) func(error, echo.Cont
37 } 112 }
38 } 113 }
39 114
40 t, err := template.New("").Funcs(funcs).ParseFS( 115 ct, _, err := contenttype.GetAcceptableMediaType(c.Request(), hIndex)
41 templates,
42 "404.tpl",
43 "40x.tpl",
44 "50x.tpl",
45 "header.tpl",
46 "footer.tpl",
47 )
48 if err != nil { 116 if err != nil {
49 he = &echo.HTTPError{ 117 c.Echo().Logger.Error("Error negotiating content type in error handler, using json")
50 Code: http.StatusInternalServerError, 118 ct = contenttype.NewMediaType("text/json")
51 Message: http.StatusText(http.StatusInternalServerError),
52 }
53 } 119 }
54 120
55 path := "50x.tpl" 121 handle, ok := handlers[ct.String()]
56 if he.Code == 404 { 122 if !ok {
57 path = "404.tpl" 123 c.Echo().Logger.Errorf("Error handler content type %s is unknown", ct.String())
58 } else if he.Code >= 400 && he.Code <= 499 { 124 handle = handleJson
59 path = "40x.tpl"
60 } 125 }
61 126
62 // Send response
63 if !c.Response().Committed { 127 if !c.Response().Committed {
64 if c.Request().Method == http.MethodHead { // Issue #608 128 if c.Request().Method == http.MethodHead { // Issue #608
65 err = c.NoContent(he.Code) 129 err = c.NoContent(he.Code)
66 } else { 130 } else {
67 buf := bytes.Buffer{} 131 err = handle(c, he)
68 if err = t.ExecuteTemplate(&buf, path, nil); err != nil {
69 err = c.String(he.Code, fmt.Sprintf("%s", he.Message))
70 }
71 c.HTMLBlob(he.Code, buf.Bytes())
72 } 132 }
73 if err != nil { 133 if err != nil {
74 c.Echo().Logger.Error(err) 134 errorWhileErroring(c, err)
75 } 135 }
76 } 136 }
77 } 137 }