-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecoder.go
More file actions
124 lines (112 loc) · 2.97 KB
/
decoder.go
File metadata and controls
124 lines (112 loc) · 2.97 KB
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
package bind
import (
"encoding/json"
"encoding/xml"
"errors"
"io"
"mime/multipart"
"net/http"
"reflect"
"sync"
"github.com/go-playground/form/v4"
)
var (
decoderMu sync.RWMutex
decoders = map[ContentType]func(*http.Request, any) error{
ContentTypeJSON: decodeJSONRequest,
ContentTypeXML: decodeXMLRequest,
ContentTypeForm: decodeFormRequest,
ContentTypeMultipart: decodeMultipartFormRequest,
}
MaxMultipartMemory int64 = 32 << 20
)
// GetDecoder - 지정된 Content-Type에 대한 디코더 함수를 반환합니다.
// 테스트 또는 동적 디코더 관리에 유용합니다.
// GetDecoder returns the decoder function for the given Content-Type.
// Useful for testing or dynamic decoder management.
func GetDecoder(ct ContentType) (func(*http.Request, any) error, bool) {
decoderMu.RLock()
defer decoderMu.RUnlock()
dec, ok := decoders[ct]
return dec, ok
}
func SetMaxMultipartMemory(size int64) {
MaxMultipartMemory = size
}
func DefaultDecoder(r *http.Request, v any) error {
ct := GetContentType(r.Header.Get("Content-Type"))
decoderMu.RLock()
fn, ok := decoders[ct]
decoderMu.RUnlock()
if ok {
return fn(r, v)
}
return errors.New("bind: unsupported content type")
}
func RegisterDecoder(ct ContentType, fn func(*http.Request, any) error) {
decoderMu.Lock()
defer decoderMu.Unlock()
decoders[ct] = fn
}
func decodeJSONRequest(r *http.Request, v any) error {
defer io.Copy(io.Discard, r.Body)
return json.NewDecoder(r.Body).Decode(v)
}
func decodeXMLRequest(r *http.Request, v any) error {
defer io.Copy(io.Discard, r.Body)
return xml.NewDecoder(r.Body).Decode(v)
}
func decodeFormRequest(r *http.Request, v any) error {
defer io.Copy(io.Discard, r.Body)
if err := r.ParseForm(); err != nil {
return err
}
decoder := form.NewDecoder()
return decoder.Decode(v, r.PostForm)
}
func decodeMultipartFormRequest(r *http.Request, v any) error {
defer io.Copy(io.Discard, r.Body)
if err := r.ParseMultipartForm(MaxMultipartMemory); err != nil {
return err
}
if err := bindFiles(r, v); err != nil {
return err
}
decoder := form.NewDecoder()
return decoder.Decode(v, r.MultipartForm.Value)
}
func bindFiles(r *http.Request, v any) error {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
return errors.New("bind: non-pointer passed to bindFiles")
}
rv = rv.Elem()
if rv.Kind() != reflect.Struct {
return nil
}
rt := rv.Type()
fileHeaderPtrType := reflect.TypeOf((*multipart.FileHeader)(nil))
fileHeaderSliceType := reflect.TypeOf(([]*multipart.FileHeader)(nil))
for i := 0; i < rv.NumField(); i++ {
field := rv.Field(i)
fieldType := rt.Field(i)
formTag := fieldType.Tag.Get("form")
if formTag == "" {
continue
}
files, ok := r.MultipartForm.File[formTag]
if !ok || len(files) == 0 {
continue
}
if !field.CanSet() {
continue
}
switch field.Type() {
case fileHeaderPtrType:
field.Set(reflect.ValueOf(files[0]))
case fileHeaderSliceType:
field.Set(reflect.ValueOf(files))
}
}
return nil
}