Refactor error handling

This commit is contained in:
Andrea Fazzi 2020-01-23 12:50:50 +01:00
parent ae15f35792
commit 819a3caad6
3 changed files with 70 additions and 28 deletions

View file

@ -13,6 +13,7 @@ import (
"strings" "strings"
"git.andreafazzi.eu/andrea/oef/config" "git.andreafazzi.eu/andrea/oef/config"
"git.andreafazzi.eu/andrea/oef/errors"
"git.andreafazzi.eu/andrea/oef/i18n" "git.andreafazzi.eu/andrea/oef/i18n"
"git.andreafazzi.eu/andrea/oef/orm" "git.andreafazzi.eu/andrea/oef/orm"
"git.andreafazzi.eu/andrea/oef/reflect" "git.andreafazzi.eu/andrea/oef/reflect"
@ -24,6 +25,19 @@ import (
"github.com/gorilla/sessions" "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 { type Handlers struct {
Config *config.ConfigT Config *config.ConfigT
@ -61,10 +75,12 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
), ),
h.JWTCookieMiddleware.Handler( h.JWTCookieMiddleware.Handler(
h.Recover( h.Recover(
h.modelHandler( newRootMiddleware(
reflect.ModelNameLowerPlural(model), h,
pattern, h.modelHandler(
)))).Methods(pattern.Methods...) reflect.ModelNameLowerPlural(model),
pattern,
))))).Methods(pattern.Methods...)
} }
// Install API paths // Install API paths
@ -75,10 +91,13 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
), ),
h.JWTHeaderMiddleware.Handler( h.JWTHeaderMiddleware.Handler(
h.Recover( h.Recover(
h.modelHandler( newRootMiddleware(
reflect.ModelNameLowerPlural(model), h,
pattern,
)))).Methods(pattern.Methods...) h.modelHandler(
reflect.ModelNameLowerPlural(model),
pattern,
))))).Methods(pattern.Methods...)
} }
// Set permissions // Set permissions
@ -265,18 +284,16 @@ func (h *Handlers) hasPermission(role, path string) bool {
return h.permissions[role][path] 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") format := r.URL.Query().Get("format")
getFn, err := h.Database.GetFunc(pattern.Path(model)) getFn, err := h.Database.GetFunc(pattern.Path(model))
if err != nil { if err != nil {
log.Println("Error:", err) return err
respondWithError(h, w, r, err)
} else { } else {
claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
role := claims["role"].(string) role := claims["role"].(string)
if !h.hasPermission(role, pattern.Path(model)) { if !h.hasPermission(role, pattern.Path(model)) {
h.setFlashMessage(w, r, "notAuthorized") return errors.NotAuthorized
h.Renderer[format].Render(w, r, h.CookieStore, fmt.Errorf("%s", "Errore di autorizzazione"))
} else { } else {
data, err := getFn(h.Database, mux.Vars(r), w, r) data, err := getFn(h.Database, mux.Vars(r), w, r)
if err != nil { 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 { 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)) postFn, err := h.Database.GetFunc(pattern.Path(model))
if err != nil { if err != nil {
respondWithError(h, w, r, err) return err
} else { } else {
claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
role := claims["role"].(string) role := claims["role"].(string)
if !h.hasPermission(role, pattern.Path(model)) { 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 { } else {
data, err = postFn(h.Database, mux.Vars(r), w, r) data, err = postFn(h.Database, mux.Vars(r), w, r)
if err != nil { if err != nil {
respondWithError(h, w, r, err) return err
} else if pattern.RedirectPattern != "" { } else if pattern.RedirectPattern != "" {
if id := mux.Vars(r)["id"]; id != "" { if id := mux.Vars(r)["id"]; id != "" {
modelId, _ := strconv.Atoi(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{} var data interface{}
respFormat := renderer.GetContentFormat(r) respFormat := renderer.GetContentFormat(r)
claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims) claims := r.Context().Value("user").(*jwt.Token).Claims.(jwt.MapClaims)
role := claims["role"].(string) role := claims["role"].(string)
if !h.hasPermission(role, pattern.Path(model)) { 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 { } else {
postFn, err := h.Database.GetFunc(pattern.Path(model)) postFn, err := h.Database.GetFunc(pattern.Path(model))
if err != nil { 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()) 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) { 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) 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 { func (h *Handlers) Create(model interface{}) http.Handler {
@ -389,25 +413,29 @@ func (h *Handlers) Read(model interface{}) http.Handler {
return http.HandlerFunc(fn) return http.HandlerFunc(fn)
} }
func (h *Handlers) modelHandler(model string, pattern config.PathPattern) http.Handler { func (h *Handlers) modelHandler(model string, pattern config.PathPattern) handlerFuncWithError {
fn := func(w http.ResponseWriter, r *http.Request) { fn := func(w http.ResponseWriter, r *http.Request) error {
var err error
// Replace the "api" prefix. // Replace the "api" prefix.
pattern.PathPattern = strings.Replace(pattern.PathPattern, "/api", "", -1) pattern.PathPattern = strings.Replace(pattern.PathPattern, "/api", "", -1)
switch r.Method { switch r.Method {
case "GET": case "GET":
h.get(w, r, model, pattern) err = h.get(w, r, model, pattern)
case "POST": case "POST":
h.post(w, r, model, pattern) err = h.post(w, r, model, pattern)
case "DELETE": 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 { func DefaultHomeHandler() http.Handler {
@ -458,3 +486,14 @@ func DefaultHomeHandler() http.Handler {
} }
return http.HandlerFunc(fn) 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)
}

View file

@ -41,5 +41,9 @@ var (
"outOfTime": map[string]string{ "outOfTime": map[string]string{
"it": "Il tempo utile per consegnare la prova è scaduto.", "it": "Il tempo utile per consegnare la prova è scaduto.",
}, },
"notAuthorized": map[string]string{
"it": "Non si è autorizzati ad accedere a questa pagina",
},
} }
) )

View file

@ -196,7 +196,6 @@ func (rend *HTMLRenderer) writeError(w http.ResponseWriter, r *http.Request, dat
if !ok { if !ok {
panic(fmt.Errorf("Error template not found! Can't proceed, sorry.")) 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") w.Header().Set("Content-Type", "text/html; charset=utf-8")