From 819a3caad69568dc80b29fe63252b7f3b5b12657 Mon Sep 17 00:00:00 2001 From: Andrea Fazzi Date: Thu, 23 Jan 2020 12:50:50 +0100 Subject: [PATCH] Refactor error handling --- handlers/handlers.go | 93 +++++++++++++++++++++++++++++++------------- i18n/i18n.go | 4 ++ renderer/renderer.go | 1 - 3 files changed, 70 insertions(+), 28 deletions(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index e693bf68..b3501fbb 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -13,6 +13,7 @@ import ( "strings" "git.andreafazzi.eu/andrea/oef/config" + "git.andreafazzi.eu/andrea/oef/errors" "git.andreafazzi.eu/andrea/oef/i18n" "git.andreafazzi.eu/andrea/oef/orm" "git.andreafazzi.eu/andrea/oef/reflect" @@ -24,6 +25,19 @@ import ( "github.com/gorilla/sessions" ) +type handlerFuncWithError func(http.ResponseWriter, *http.Request) error + +type rootMiddleware struct { + h *Handlers + fn handlerFuncWithError +} + +func newRootMiddleware(h *Handlers, fn func(http.ResponseWriter, *http.Request) error) *rootMiddleware { + return &rootMiddleware{h, fn} +} + +// type rootHandler func(http.ResponseWriter, *http.Request) error + type Handlers struct { Config *config.ConfigT @@ -61,10 +75,12 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { ), h.JWTCookieMiddleware.Handler( h.Recover( - h.modelHandler( - reflect.ModelNameLowerPlural(model), - pattern, - )))).Methods(pattern.Methods...) + newRootMiddleware( + h, + h.modelHandler( + reflect.ModelNameLowerPlural(model), + pattern, + ))))).Methods(pattern.Methods...) } // Install API paths @@ -75,10 +91,13 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) { ), h.JWTHeaderMiddleware.Handler( h.Recover( - h.modelHandler( - reflect.ModelNameLowerPlural(model), - pattern, - )))).Methods(pattern.Methods...) + newRootMiddleware( + h, + + h.modelHandler( + reflect.ModelNameLowerPlural(model), + pattern, + ))))).Methods(pattern.Methods...) } // Set permissions @@ -265,18 +284,16 @@ func (h *Handlers) hasPermission(role, path string) bool { return h.permissions[role][path] } -func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) { +func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) error { format := r.URL.Query().Get("format") getFn, err := h.Database.GetFunc(pattern.Path(model)) if err != nil { - log.Println("Error:", err) - respondWithError(h, w, r, err) + return err } else { claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) role := claims["role"].(string) if !h.hasPermission(role, pattern.Path(model)) { - h.setFlashMessage(w, r, "notAuthorized") - h.Renderer[format].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) + return errors.NotAuthorized } else { data, err := getFn(h.Database, mux.Vars(r), w, r) if err != nil { @@ -287,6 +304,7 @@ func (h *Handlers) get(w http.ResponseWriter, r *http.Request, model string, pat } } + return nil } func (h *Handlers) post(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) error { @@ -299,17 +317,17 @@ func (h *Handlers) post(w http.ResponseWriter, r *http.Request, model string, pa postFn, err := h.Database.GetFunc(pattern.Path(model)) if err != nil { - respondWithError(h, w, r, err) + return err } else { claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) role := claims["role"].(string) if !h.hasPermission(role, pattern.Path(model)) { - h.Renderer[respFormat].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) + return errors.NotAuthorized } else { data, err = postFn(h.Database, mux.Vars(r), w, r) if err != nil { - respondWithError(h, w, r, err) + return err } else if pattern.RedirectPattern != "" { if id := mux.Vars(r)["id"]; id != "" { modelId, _ := strconv.Atoi(id) @@ -327,16 +345,15 @@ func (h *Handlers) post(w http.ResponseWriter, r *http.Request, model string, pa } -func (h *Handlers) delete(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) { +func (h *Handlers) delete(w http.ResponseWriter, r *http.Request, model string, pattern config.PathPattern) error { var data interface{} respFormat := renderer.GetContentFormat(r) claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) - role := claims["role"].(string) if !h.hasPermission(role, pattern.Path(model)) { - h.Renderer[respFormat].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione")) + return errors.NotAuthorized } else { postFn, err := h.Database.GetFunc(pattern.Path(model)) if err != nil { @@ -357,12 +374,19 @@ func (h *Handlers) delete(w http.ResponseWriter, r *http.Request, model string, h.Renderer[respFormat].Render(w, r, h.CookieStore, data.(orm.IDer).GetID()) } } + + return nil } func respondWithError(h *Handlers, w http.ResponseWriter, r *http.Request, err error) { - respFormat := renderer.GetContentFormat(r) + var format string + + format = r.URL.Query().Get("format") + if format == "" { + format = renderer.GetContentFormat(r) + } w.WriteHeader(http.StatusInternalServerError) - h.Renderer[respFormat].Render(w, r, h.CookieStore, err) + h.Renderer[format].Render(w, r, h.CookieStore, err) } func (h *Handlers) Create(model interface{}) http.Handler { @@ -389,25 +413,29 @@ func (h *Handlers) Read(model interface{}) http.Handler { return http.HandlerFunc(fn) } -func (h *Handlers) modelHandler(model string, pattern config.PathPattern) http.Handler { - fn := func(w http.ResponseWriter, r *http.Request) { +func (h *Handlers) modelHandler(model string, pattern config.PathPattern) handlerFuncWithError { + fn := func(w http.ResponseWriter, r *http.Request) error { + + var err error // Replace the "api" prefix. pattern.PathPattern = strings.Replace(pattern.PathPattern, "/api", "", -1) switch r.Method { case "GET": - h.get(w, r, model, pattern) + err = h.get(w, r, model, pattern) case "POST": - h.post(w, r, model, pattern) + err = h.post(w, r, model, pattern) case "DELETE": - h.delete(w, r, model, pattern) + err = h.delete(w, r, model, pattern) } + + return err } - return http.HandlerFunc(fn) + return handlerFuncWithError(fn) } func DefaultHomeHandler() http.Handler { @@ -458,3 +486,14 @@ func DefaultHomeHandler() http.Handler { } return http.HandlerFunc(fn) } + +func (m *rootMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { + err := m.fn(w, r) // Call handler function + if err == nil { + return + } + // This is where our error handling logic starts. + log.Printf("An error accured: %v", err) // Log the error. + + respondWithError(m.h, w, r, err) +} diff --git a/i18n/i18n.go b/i18n/i18n.go index 4f1514a8..c0b4bcec 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -41,5 +41,9 @@ var ( "outOfTime": map[string]string{ "it": "Il tempo utile per consegnare la prova è scaduto.", }, + "notAuthorized": map[string]string{ + "it": "Non si è autorizzati ad accedere a questa pagina", + }, + } ) diff --git a/renderer/renderer.go b/renderer/renderer.go index aeec7f4f..a8607f4d 100644 --- a/renderer/renderer.go +++ b/renderer/renderer.go @@ -196,7 +196,6 @@ func (rend *HTMLRenderer) writeError(w http.ResponseWriter, r *http.Request, dat if !ok { panic(fmt.Errorf("Error template not found! Can't proceed, sorry.")) } - log.Println(data.(*htmlTemplateData).Data.(error)) } w.Header().Set("Content-Type", "text/html; charset=utf-8")