forked from bytedance/go-tagexpr
/
receiver.go
172 lines (152 loc) · 3.4 KB
/
receiver.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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package binding
import (
"net/http"
"net/url"
"reflect"
"github.com/bytedance/go-tagexpr"
)
type in uint8
const (
undefined in = iota
path
form
query
cookie
header
protobuf
json
raw_body
default_val
maxIn
)
var (
allIn = func() []in {
a := []in{}
for i := undefined + 1; i < maxIn; i++ {
a = append(a, i)
}
return a
}()
sortedDefaultIn = func() []in {
var a []in
for i := undefined + 1; i < raw_body; i++ {
a = append(a, i)
}
return a
}()
)
type codec in
const (
bodyUnsupport = codec(0)
bodyForm = codec(form)
bodyJSON = codec(json)
bodyProtobuf = codec(protobuf)
)
type receiver struct {
hasPath, hasQuery, hasBody, hasCookie, hasVd bool
params []*paramInfo
looseZeroMode bool
}
func (r *receiver) assginIn(i in, v bool) {
switch i {
case path:
r.hasPath = r.hasPath || v
case query:
r.hasQuery = r.hasQuery || v
case form, json, protobuf:
r.hasBody = r.hasBody || v
case cookie:
r.hasCookie = r.hasCookie || v
}
}
func (r *receiver) getParam(fieldSelector string) *paramInfo {
for _, p := range r.params {
if p.fieldSelector == fieldSelector {
return p
}
}
return nil
}
func (r *receiver) getOrAddParam(fh *tagexpr.FieldHandler, bindErrFactory func(failField, msg string) error) *paramInfo {
fieldSelector := fh.StringSelector()
p := r.getParam(fieldSelector)
if p != nil {
return p
}
p = ¶mInfo{
fieldSelector: fieldSelector,
structField: fh.StructField(),
omitIns: make(map[in]bool, maxIn),
bindErrFactory: bindErrFactory,
looseZeroMode: r.looseZeroMode,
}
r.params = append(r.params, p)
return p
}
func (r *receiver) getBodyInfo(req *http.Request) (codec, []byte, error) {
if r.hasBody {
return getBodyInfo(req)
}
return bodyUnsupport, nil, nil
}
func (r *receiver) prebindBody(pointer interface{}, val reflect.Value, bodyCodec codec, bodyBytes []byte) error {
switch bodyCodec {
case bodyJSON:
return bindJSON(pointer, bodyBytes)
case bodyProtobuf:
return bindProtobuf(pointer, bodyBytes)
default:
return nil
}
}
const (
defaultMaxMemory = 32 << 20 // 32 MB
)
func (r *receiver) getQuery(req *http.Request) url.Values {
if r.hasQuery {
return req.URL.Query()
}
return nil
}
func (r *receiver) getCookies(req *http.Request) []*http.Cookie {
if r.hasCookie {
return req.Cookies()
}
return nil
}
func (r *receiver) initParams() {
names := make(map[string][maxIn]string, len(r.params))
for _, p := range r.params {
if p.structField.Anonymous {
continue
}
a := [maxIn]string{}
for _, paramIn := range allIn {
a[paramIn] = p.name(paramIn)
}
names[p.fieldSelector] = a
}
for _, p := range r.params {
paths, _ := tagexpr.FieldSelector(p.fieldSelector).Split()
for _, info := range p.tagInfos {
var fs string
for _, s := range paths {
if fs == "" {
fs = s
} else {
fs = tagexpr.JoinFieldSelector(fs, s)
}
name := names[fs][info.paramIn]
if name != "" {
info.namePath = name + "."
}
}
info.namePath = info.namePath + p.name(info.paramIn)
info.requiredError = p.bindErrFactory(info.namePath, "missing required parameter")
info.typeError = p.bindErrFactory(info.namePath, "parameter type does not match binding data")
info.cannotError = p.bindErrFactory(info.namePath, "parameter cannot be bound")
info.contentTypeError = p.bindErrFactory(info.namePath, "does not support binding to the content type body")
}
p.setDefaultVal()
}
}