Improved error handling

This commit is contained in:
Eduard Urbach 2023-07-21 23:23:49 +02:00
parent 7da8fffb90
commit 72ad02a2e2
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
3 changed files with 33 additions and 8 deletions

View File

@ -1,6 +1,7 @@
package server package server
import ( import (
"errors"
"io" "io"
"net/http" "net/http"
"unsafe" "unsafe"
@ -12,8 +13,9 @@ const maxParams = 16
// Context represents the interface for a request & response context. // Context represents the interface for a request & response context.
type Context interface { type Context interface {
Bytes([]byte) error Bytes([]byte) error
Error(int, error) error Error(status int, messages ...any) error
Reader(io.Reader) error Reader(io.Reader) error
SetStatus(status int)
String(string) error String(string) error
} }
@ -42,9 +44,20 @@ func (ctx *context) Bytes(body []byte) error {
} }
// Error is used for sending error messages to the client. // Error is used for sending error messages to the client.
func (ctx *context) Error(status int, err error) error { func (ctx *context) Error(status int, messages ...any) error {
ctx.response.WriteHeader(status) var combined []error
return err
for _, msg := range messages {
switch err := msg.(type) {
case error:
combined = append(combined, err)
case string:
combined = append(combined, errors.New(err))
}
}
ctx.SetStatus(status)
return errors.Join(combined...)
} }
// Reader sends the contents of the io.Reader without creating an in-memory copy. // Reader sends the contents of the io.Reader without creating an in-memory copy.
@ -53,6 +66,11 @@ func (ctx *context) Reader(reader io.Reader) error {
return err return err
} }
// SetStatus writes the header with the given HTTP status code.
func (ctx *context) SetStatus(status int) {
ctx.response.WriteHeader(status)
}
// String responds with the given string. // String responds with the given string.
func (ctx *context) String(body string) error { func (ctx *context) String(body string) error {
slice := unsafe.Slice(unsafe.StringData(body), len(body)) slice := unsafe.Slice(unsafe.StringData(body), len(body))

View File

@ -1,7 +1,8 @@
package server package server
import ( import (
"fmt" "io"
"log"
"net/http" "net/http"
"git.akyoto.dev/go/router" "git.akyoto.dev/go/router"
@ -34,7 +35,7 @@ func (server *Server) ServeHTTP(response http.ResponseWriter, request *http.Requ
if handler == nil { if handler == nil {
response.WriteHeader(http.StatusNotFound) response.WriteHeader(http.StatusNotFound)
fmt.Fprint(response, http.StatusText(http.StatusNotFound)) response.(io.StringWriter).WriteString(http.StatusText(http.StatusNotFound))
contextPool.Put(ctx) contextPool.Put(ctx)
return return
} }
@ -42,7 +43,8 @@ func (server *Server) ServeHTTP(response http.ResponseWriter, request *http.Requ
err := handler(ctx) err := handler(ctx)
if err != nil { if err != nil {
fmt.Fprint(response, err.Error()) response.(io.StringWriter).WriteString(err.Error())
log.Println(request.URL, err)
} }
contextPool.Put(ctx) contextPool.Put(ctx)

View File

@ -24,7 +24,11 @@ func TestRouter(t *testing.T) {
}) })
s.Get("/error", func(ctx server.Context) error { s.Get("/error", func(ctx server.Context) error {
return ctx.Error(http.StatusUnauthorized, errors.New("Not logged in")) return ctx.Error(http.StatusUnauthorized, "Not logged in")
})
s.Get("/error2", func(ctx server.Context) error {
return ctx.Error(http.StatusUnauthorized, "Not logged in", errors.New("Missing auth token"))
}) })
s.Get("/reader", func(ctx server.Context) error { s.Get("/reader", func(ctx server.Context) error {
@ -43,6 +47,7 @@ func TestRouter(t *testing.T) {
{URL: "/", Status: http.StatusOK, Body: "Hello"}, {URL: "/", Status: http.StatusOK, Body: "Hello"},
{URL: "/blog/post", Status: http.StatusOK, Body: "Hello"}, {URL: "/blog/post", Status: http.StatusOK, Body: "Hello"},
{URL: "/error", Status: http.StatusUnauthorized, Body: "Not logged in"}, {URL: "/error", Status: http.StatusUnauthorized, Body: "Not logged in"},
{URL: "/error2", Status: http.StatusUnauthorized, Body: "Not logged in\nMissing auth token"},
{URL: "/not-found", Status: http.StatusNotFound, Body: http.StatusText(http.StatusNotFound)}, {URL: "/not-found", Status: http.StatusNotFound, Body: http.StatusText(http.StatusNotFound)},
{URL: "/reader", Status: http.StatusOK, Body: "Hello"}, {URL: "/reader", Status: http.StatusOK, Body: "Hello"},
{URL: "/string", Status: http.StatusOK, Body: "Hello"}, {URL: "/string", Status: http.StatusOK, Body: "Hello"},