Skip to content

Commit 5695f35

Browse files
committed
chore: centralized error formatting
1 parent 741ef5d commit 5695f35

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

cmd/gateway/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ func main() {
6666
}
6767
// Setup
6868
e := echo.New()
69+
e.HTTPErrorHandler = revproxy.ErrorHandler
6970
e.Pre(middleware.RequestID(), middleware.RemoveTrailingSlash(), revproxy.UiServerPathRewrite())
7071
e.Use(middleware.Recover())
7172
// Sentry middleware

internal/revproxy/main.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
package revproxy
44

55
import (
6+
"errors"
67
"fmt"
8+
"net/http"
79
"path"
10+
"strings"
811

912
"github.com/SwissDataScienceCenter/renku-gateway/internal/config"
1013
"github.com/SwissDataScienceCenter/renku-gateway/internal/models"
@@ -151,3 +154,56 @@ func NewServer(options ...RevproxyOption) (*Revproxy, error) {
151154
}
152155
return &server, nil
153156
}
157+
158+
// The same error content as the data services API
159+
const gwBaseErrorCode int = 6000
160+
161+
type errorContent struct {
162+
Code int `json:"code"`
163+
Message string `json:"message"`
164+
Detail string `json:"detail,omitempty"`
165+
TraceId string `json:"trace_id,omitempty"`
166+
}
167+
168+
type errorResponse struct {
169+
Error errorContent `json:"error"`
170+
}
171+
172+
// Adapted from https://echo.labstack.com/docs/error-handling
173+
func ErrorHandler(err error, c echo.Context) {
174+
if c.Response() != nil && c.Response().Committed {
175+
return // response has been already sent to the client by handler or some middleware
176+
}
177+
178+
accept := c.Request().Header.Get("Accept")
179+
isHTML := strings.Contains(accept, echo.MIMETextHTML)
180+
181+
// If the accept header is html then we fall back to the default handler (for now).
182+
// If the acceptt header is not html or is blank we return json
183+
if isHTML {
184+
c.Echo().DefaultHTTPErrorHandler(err, c)
185+
return
186+
}
187+
188+
code := http.StatusInternalServerError
189+
message := err.Error()
190+
var he *echo.HTTPError
191+
if errors.As(err, &he) { // find error in an error chain that implements HTTPError
192+
if tmp := he.Code; tmp != 0 {
193+
code = tmp
194+
}
195+
if msg := fmt.Sprintf("%v", he.Message); len(msg) > 0 {
196+
message = msg
197+
}
198+
}
199+
200+
var cErr error
201+
if c.Request().Method == http.MethodHead {
202+
cErr = c.NoContent(code)
203+
} else {
204+
cErr = c.JSON(code, errorResponse{Error: errorContent{Code: gwBaseErrorCode + code, Message: message}})
205+
}
206+
if cErr != nil {
207+
c.Logger().Error("failed to send error page to client", "error", errors.Join(err, cErr))
208+
}
209+
}

0 commit comments

Comments
 (0)