92 lines
2.1 KiB
Go
92 lines
2.1 KiB
Go
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,
|
|
}
|
|
}
|