From 83ed018bd393d6385a28ad9799e5da6c87250de3 Mon Sep 17 00:00:00 2001 From: Daniil Tsivinsky Date: Tue, 17 Mar 2026 12:42:08 +0300 Subject: [PATCH] return `Server.Error()` --- main.go | 45 +++++++++++++++------------------------------ server.go | 46 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/main.go b/main.go index 7177571..a5aaa12 100644 --- a/main.go +++ b/main.go @@ -30,13 +30,11 @@ func main() { Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - srv.Error(w, "empty body", err, 400) - return nil + return srv.Error("empty body", err, 400) } if body.Email == "" || body.Password == "" { - srv.Error(w, "email or password missing", nil, 400) - return nil + return srv.Error("email or password missing", nil, 400) } hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10) @@ -50,8 +48,7 @@ func main() { } if err := user.Create(db); err != nil { - srv.Error(w, "failed to create user", err, 400) - return nil + return srv.Error("failed to create user", err, 400) } if err := user.FindByID(db); err != nil { @@ -74,24 +71,20 @@ func main() { Password string `json:"password"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil { - srv.Error(w, "empty body", err, 400) - return nil + return srv.Error("empty body", err, 400) } if body.Email == "" || body.Password == "" { - srv.Error(w, "email or password missing", nil, 400) - return nil + return srv.Error("email or password missing", nil, 400) } user := &model.User{Email: body.Email} if err := user.FindByEmail(db); err != nil { - srv.Error(w, "user not found", err, 404) - return nil + return srv.Error("user not found", err, 404) } if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password)); err != nil { - srv.Error(w, "invalid password", nil, 400) - return nil + return srv.Error("invalid password", nil, 400) } expiryTime := time.Now().Add(time.Hour * 24 * 30) @@ -107,14 +100,12 @@ func main() { srv.Handle("POST /api/images", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { - srv.Error(w, "unauthorized", err, 401) - return nil + return srv.Error("unauthorized", err, 401) } user := &model.User{Model: model.Model{ID: userId}} if err := user.FindByID(db); err != nil { - srv.Error(w, "user not found", nil, 401) - return nil + return srv.Error("user not found", nil, 401) } data, err := io.ReadAll(r.Body) @@ -128,8 +119,7 @@ func main() { ContentType: r.Header.Get("Content-Type"), } if err := img.Create(db); err != nil { - srv.Error(w, "failed to save image to database: %v", err, 400) - return nil + return srv.Error("failed to save image to database: %v", err, 400) } return srv.JSON(w, img, 201) @@ -138,14 +128,12 @@ func main() { srv.Handle("GET /api/images", func(w http.ResponseWriter, r *http.Request) error { userId, err := auth.GetUserIdFromRequest(r) if err != nil { - srv.Error(w, "unauthorized", err, 401) - return nil + return srv.Error("unauthorized", err, 401) } rows, err := db.Queryx("SELECT * FROM images WHERE user_id = ?", userId) if err != nil { - srv.Error(w, "images not found", err, 400) - return nil + return srv.Error("images not found", err, 400) } images := []model.Image{} @@ -164,8 +152,7 @@ func main() { srv.Handle("GET /images/{id}", func(w http.ResponseWriter, r *http.Request) error { img := &model.Image{ID: r.PathValue("id")} if err := img.FindByID(db); err != nil { - srv.Error(w, "image not found", nil, 404) - return nil + return srv.Error("image not found", nil, 404) } w.Header().Set("Content-Type", img.ContentType) @@ -177,13 +164,11 @@ func main() { srv.Handle("DELETE /api/images/{id}", func(w http.ResponseWriter, r *http.Request) error { img := &model.Image{ID: r.PathValue("id")} if err := img.FindByID(db); err != nil { - srv.Error(w, "image not found", nil, 404) - return nil + return srv.Error("image not found", nil, 404) } if err := img.DeleteByID(db); err != nil { - srv.Error(w, "failed to delete image: %v", err, 500) - return nil + return srv.Error("failed to delete image: %v", err, 500) } return srv.JSON(w, struct { diff --git a/server.go b/server.go index 95027bf..e47841b 100644 --- a/server.go +++ b/server.go @@ -5,6 +5,16 @@ import ( "net/http" ) +type ApiError struct { + Message string `json:"message"` + Err error `json:"error"` + Status int +} + +func (a *ApiError) Error() string { + return a.Message +} + type Server struct { addr string mux *http.ServeMux @@ -19,8 +29,23 @@ func NewServer(addr string) *Server { func (s *Server) Handle(pattern string, handler func(w http.ResponseWriter, r *http.Request) error) { s.mux.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) { - if err := handler(w, r); err != nil { - s.Error(w, "something bad happened", err, 500) + err := handler(w, r) + if err != nil { + if e, ok := err.(*ApiError); ok { + var nestedErr *string + if e.Err != nil { + nestedErr = new(e.Err.Error()) + } + s.JSON(w, map[string]any{ + "message": e.Message, + "error": nestedErr, + }, e.Status) + } else { + s.JSON(w, map[string]any{ + "message": "something bad happened", + "error": err.Error(), + }, 500) + } } }) } @@ -31,19 +56,12 @@ func (s *Server) JSON(w http.ResponseWriter, data any, status int) error { return json.NewEncoder(w).Encode(data) } -func (s *Server) Error(w http.ResponseWriter, msg string, err error, status int) { - var e *string - if err != nil { - e = new(err.Error()) - } - - s.JSON(w, struct { - Message string `json:"message"` - Error *string `json:"error"` - }{ +func (s *Server) Error(msg string, err error, status int) error { + return &ApiError{ Message: msg, - Error: e, - }, status) + Err: err, + Status: status, + } } func (s *Server) ListenAndServe() error {