From 6079b1d963f34ada5c4b25363f2319901e283936 Mon Sep 17 00:00:00 2001 From: "Gabriel A. Giovanini" Date: Sat, 8 Jun 2024 00:01:44 +0200 Subject: feat: Add error handling --- pkg/ext/compression.go | 2 +- pkg/ext/log.go | 53 +++++++++++++++++++++++++++++++++++++ pkg/ext/router.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 pkg/ext/log.go create mode 100644 pkg/ext/router.go (limited to 'pkg/ext') diff --git a/pkg/ext/compression.go b/pkg/ext/compression.go index 92144b8..57ad49a 100644 --- a/pkg/ext/compression.go +++ b/pkg/ext/compression.go @@ -24,7 +24,7 @@ type CompressionResponseWriter struct { compressWriter io.Writer } -func Compress(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { +func Compress(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if accept, ok := r.Header["Accept-Encoding"]; ok { if compress, algo := GetCompressionWriter(u.FirstOrZero(accept), w); algo != "" { diff --git a/pkg/ext/log.go b/pkg/ext/log.go new file mode 100644 index 0000000..a9d26a9 --- /dev/null +++ b/pkg/ext/log.go @@ -0,0 +1,53 @@ +package ext + +import ( + "log/slog" + "net/http" + "time" +) + +type statusWraper struct { + statusCode int + innerWriter http.ResponseWriter +} + +func (s *statusWraper) Header() http.Header { + return s.innerWriter.Header() +} + +func (s *statusWraper) Write(b []byte) (int, error) { + return s.innerWriter.Write(b) +} + +func (s *statusWraper) WriteHeader(statusCode int) { + s.statusCode = statusCode + s.innerWriter.WriteHeader(statusCode) +} + +func (s *statusWraper) StatusCode() int { + if s.statusCode == 0 { + return 200 + } + return s.statusCode +} + +func wrap(w http.ResponseWriter) *statusWraper { + return &statusWraper{ + innerWriter: w, + } +} + +func Log(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + t := time.Now() + s := wrap(w) + next(s, r) + slog.Info( + "Http request", + "method", r.Method, + "code", s.StatusCode(), + "path", r.URL, + "elapsed", time.Since(t), + ) + } +} diff --git a/pkg/ext/router.go b/pkg/ext/router.go new file mode 100644 index 0000000..5d22814 --- /dev/null +++ b/pkg/ext/router.go @@ -0,0 +1,72 @@ +package ext + +import ( + "errors" + "fmt" + "net/http" + + "git.gabrielgio.me/cerrado/pkg/service" + "git.gabrielgio.me/cerrado/templates" +) + +type ( + Router struct { + middlewares []Middleware + router *http.ServeMux + } + Middleware func(next http.HandlerFunc) http.HandlerFunc + ErrorRequestHandler func(w http.ResponseWriter, r *http.Request) error +) + +func NewRouter() *Router { + return &Router{ + router: http.NewServeMux(), + } +} +func (r *Router) Handler() http.Handler { + return r.router +} + +func (r *Router) AddMiddleware(middleware Middleware) { + r.middlewares = append(r.middlewares, middleware) +} + +func wrapError(next ErrorRequestHandler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if err := next(w, r); err != nil { + if errors.Is(err, service.RepositoryNotFoundErr) { + NotFound(w) + } else { + InternalServerError(w, err) + } + } + } +} + +func (r *Router) run(next ErrorRequestHandler) http.HandlerFunc { + return func(w http.ResponseWriter, re *http.Request) { + req := wrapError(next) + for _, r := range r.middlewares { + req = r(req) + } + req(w, re) + } +} + +func (r *Router) HandleFunc(path string, handler ErrorRequestHandler) { + r.router.HandleFunc(path, r.run(handler)) +} + +func NotFound(w http.ResponseWriter) { + w.WriteHeader(http.StatusNotFound) + templates.WritePageTemplate(w, &templates.ErrorPage{ + Message: "Not Found", + }) +} + +func InternalServerError(w http.ResponseWriter, err error) { + w.WriteHeader(http.StatusInternalServerError) + templates.WritePageTemplate(w, &templates.ErrorPage{ + Message: fmt.Sprintf("Internal Server Error:\n%s", err.Error()), + }) +} -- cgit v1.2.3