diff options
Diffstat (limited to 'handler.go')
-rw-r--r-- | handler.go | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..c7f9b66 --- /dev/null +++ b/handler.go @@ -0,0 +1,91 @@ +package mux + +import ( + "context" + "encoding/json" + "net/http" + + "go.neonxp.ru/mux/ctxlib" +) + +// Handler API handler and returns standard http.HandlerFunc function +func Handler[RQ any, RS any](handler func(ctx context.Context, request *RQ) (RS, error)) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + req := new(RQ) + ctx := r.Context() + ctx = context.WithValue(ctx, ctxlib.Request, r) + ctx = context.WithValue(ctx, ctxlib.Method, r.Method) + ctx = context.WithValue(ctx, ctxlib.Headers, r.Header) + + switch r.Method { + case http.MethodPost, http.MethodPatch, http.MethodDelete, http.MethodPut: + if err := Bind(r, req); err != nil { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(err.Error())) + return + } + } + resp, err := handler(ctx, req) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return + } + + statusCode := http.StatusOK + contentType := "application/json" + var body []byte + + if v, ok := (any)(resp).(WithContentType); ok { + contentType = v.ContentType() + } + if v, ok := (any)(resp).(WithHTTPStatus); ok { + statusCode = v.Status() + } + if v, ok := (any)(resp).(Renderer); ok { + err = v.Render(ctx, w) + return + } + + body, err = json.Marshal(resp) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + _, _ = w.Write([]byte(err.Error())) + return + } + w.WriteHeader(statusCode) + w.Header().Set("Content-Type", contentType) + w.Write(body) + + } +} + +type NilRequest struct{} + +// WithContentType returns custom content type for response +type WithContentType interface { + ContentType() string +} + +// WithHTTPStatus returns custom status code +type WithHTTPStatus interface { + Status() int +} + +type RedirectResponse struct { + Code int + Location string +} + +func (rr *RedirectResponse) Render(ctx context.Context, w http.ResponseWriter) error { + w.Header().Add("Location", rr.Location) + w.WriteHeader(rr.Code) + return nil +} + +func Redirect(code int, location string) *RedirectResponse { + return &RedirectResponse{ + Code: code, + Location: location, + } +} |