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, } }