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"
"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(
newRootMiddleware(
h,
h.modelHandler(
reflect.ModelNameLowerPlural(model),
pattern,
)))).Methods(pattern.Methods...)
))))).Methods(pattern.Methods...)
}
// Install API paths
@ -75,10 +91,13 @@ func (h *Handlers) generateModelHandlers(r *mux.Router, model interface{}) {
),
h.JWTHeaderMiddleware.Handler(
h.Recover(
newRootMiddleware(
h,
h.modelHandler(
reflect.ModelNameLowerPlural(model),
pattern,
)))).Methods(pattern.Methods...)
))))).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 http.HandlerFunc(fn)
return err
}
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)
}

View file

@ -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",
},
}
)

View file

@ -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")