-
Notifications
You must be signed in to change notification settings - Fork 0
/
handler.go
89 lines (77 loc) · 2.09 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package tanukirpc
import (
"log/slog"
"net/http"
"time"
"github.com/go-chi/chi/v5/middleware"
)
type Handler[Reg any] interface {
build(r *Router[Reg]) http.HandlerFunc
}
func NewHandler[Req any, Res any, Reg any](h HandlerFunc[Req, Res, Reg]) Handler[Reg] {
return &handler[Req, Res, Reg]{h: h}
}
type HandlerFunc[Req any, Res any, Reg any] func(Context[Reg], Req) (Res, error)
type handler[Req any, Res any, T any] struct {
h HandlerFunc[Req, Res, T]
}
func (h *handler[Req, Res, Reg]) build(r *Router[Reg]) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
ww := middleware.NewWrapResponseWriter(w, req.ProtoMajor)
t1 := time.Now()
var t2 time.Time
var lerr error
defer func() {
if t2.IsZero() {
t2 = time.Now()
}
if err := r.accessLoggerLog(req.Context(), ww, req, lerr, t1, t2); err != nil {
r.logger.ErrorContext(req.Context(), "access log error", slog.Any("error", err))
}
}()
var reqBody Req
if err := r.codec.Decode(req, &reqBody); err != nil {
r.errorHooker.OnError(ww, req, r.logger, r.codec, err)
lerr = err
return
}
if vreq, ok := canValidate(reqBody); ok {
if err := vreq.Validate(); err != nil {
ve := &ValidateError{err: err}
r.errorHooker.OnError(ww, req, r.logger, r.codec, ve)
lerr = err
return
}
}
ctx, err := r.contextFactory.Build(ww, req)
if err != nil {
r.errorHooker.OnError(ww, req, r.logger, r.codec, err)
lerr = err
return
}
res, err := h.h(ctx, reqBody)
if err != nil {
r.errorHooker.OnError(ww, req, r.logger, r.codec, err)
lerr = err
return
}
if err := ctx.DeferDo(DeferDoTimingBeforeResponse); err != nil {
r.errorHooker.OnError(ww, req, r.logger, r.codec, err)
lerr = err
return
}
if ww.Status() == 0 {
if err := r.codec.Encode(ww, req, res); err != nil {
r.errorHooker.OnError(ww, req, r.logger, r.codec, err)
lerr = err
return
}
}
t2 = time.Now()
if err := ctx.DeferDo(DeferDoTimingAfterResponse); err != nil {
r.logger.ErrorContext(ctx, "defer do error", slog.Any("error", err))
lerr = err
return
}
}
}