-
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
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
-
336
-
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-
426
-
427
-
428
-
429
-
430
-
431
-
432
-
433
-
434
-
435
-
436
-
437
-
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
-
447
-
448
-
449
-
450
-
451
-
452
-
453
-
454
-
455
-
456
-
457
-
458
-
459
-
460
-
461
-
462
-
463
-
464
-
465
-
466
-
467
-
468
-
469
-
470
-
471
-
472
-
473
-
474
-
475
-
476
-
477
-
478
-
479
-
480
-
481
-
482
-
483
-
484
-
485
-
486
-
487
-
488
-
489
-
490
-
491
-
492
-
493
-
494
-
495
-
496
-
497
-
498
-
499
-
500
-
501
-
502
-
503
-
504
-
505
-
506
-
507
-
508
-
509
-
510
-
511
-
512
-
513
-
514
-
515
-
516
-
517
-
518
-
519
-
520
-
521
-
522
-
523
-
524
-
525
-
526
-
527
-
528
-
529
-
530
-
531
-
532
-
533
-
534
-
535
-
536
-
537
-
538
-
539
-
540
-
541
-
542
-
543
-
544
-
545
-
546
-
547
-
548
-
549
-
550
-
551
-
552
-
553
-
554
-
555
-
556
-
557
-
558
-
559
-
560
-
561
-
562
-
563
-
564
-
565
-
566
-
567
-
568
-
569
-
570
-
571
-
572
-
573
-
574
-
575
-
576
-
577
-
578
-
579
-
580
-
581
-
582
-
583
-
584
-
585
-
586
-
587
-
588
-
589
-
590
-
591
-
592
-
593
-
594
-
595
-
596
-
597
-
598
-
599
-
600
-
601
-
602
-
603
-
604
-
605
-
606
-
607
-
608
-
609
-
610
-
611
-
612
-
613
-
614
-
615
-
616
-
617
-
618
-
619
-
620
-
621
-
622
-
623
-
624
-
625
-
626
-
627
-
628
-
629
-
630
-
631
-
632
-
633
-
634
-
635
-
636
-
637
-
638
-
639
-
640
-
641
-
642
-
643
-
644
-
645
-
646
-
647
-
648
-
649
-
650
-
651
-
652
-
653
-
654
-
655
-
656
-
657
-
658
-
659
-
660
-
661
-
662
-
663
-
664
-
665
-
666
-
667
-
668
-
669
-
670
-
671
-
672
-
673
-
674
-
675
-
676
-
677
-
678
-
679
-
680
-
681
-
682
-
683
-
684
-
685
-
686
-
687
-
688
-
689
-
690
-
691
-
692
-
693
-
694
-
695
-
696
-
697
-
698
-
699
-
700
-
701
-
702
-
703
-
704
-
705
-
706
-
707
-
708
-
709
-
710
// Copyright 2014 Martini Authors
// Copyright 2014 The Macaron Authors
// Copyright 2020 The Gitea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// Package binding is a middleware that provides request data binding and validation for Chi.
package binding
import (
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
"unicode/utf8"
"github.com/goccy/go-json"
"github.com/unknwon/com"
)
// Bind wraps up the functionality of the Form and Json middleware
// according to the Content-Type and verb of the request.
// A Content-Type is required for POST and PUT requests.
// Bind invokes the ErrorHandler middleware to bail out if errors
// occurred. If you want to perform your own error handling, use
// Form or Json middleware directly. An interface pointer can
// be added as a second argument in order to map the struct to
// a specific interface.
func Bind(req *http.Request, obj interface{}) Errors {
contentType := req.Header.Get("Content-Type")
if req.Method == "POST" || req.Method == "PUT" || len(contentType) > 0 {
switch {
case strings.Contains(contentType, "form-urlencoded"):
return Form(req, obj)
case strings.Contains(contentType, "multipart/form-data"):
return MultipartForm(req, obj)
case strings.Contains(contentType, "json"):
return JSON(req, obj)
default:
var errors Errors
if contentType == "" {
errors.Add([]string{}, ERR_CONTENT_TYPE, "Empty Content-Type")
} else {
errors.Add([]string{}, ERR_CONTENT_TYPE, "Unsupported Content-Type")
}
return errors
}
} else {
return Form(req, obj)
}
}
const (
_JSON_CONTENT_TYPE = "application/json; charset=utf-8"
STATUS_UNPROCESSABLE_ENTITY = 422
)
// errorHandler simply counts the number of errors in the
// context and, if more than 0, writes a response with an
// error code and a JSON payload describing the errors.
// The response will have a JSON content-type.
// Middleware remaining on the stack will not even see the request
// if, by this point, there are any errors.
// This is a "default" handler, of sorts, and you are
// welcome to use your own instead. The Bind middleware
// invokes this automatically for convenience.
func errorHandler(errs Errors, rw http.ResponseWriter) {
if len(errs) > 0 {
rw.Header().Set("Content-Type", _JSON_CONTENT_TYPE)
if errs.Has(ERR_DESERIALIZATION) {
rw.WriteHeader(http.StatusBadRequest)
} else if errs.Has(ERR_CONTENT_TYPE) {
rw.WriteHeader(http.StatusUnsupportedMediaType)
} else {
rw.WriteHeader(STATUS_UNPROCESSABLE_ENTITY)
}
errOutput, _ := json.Marshal(errs)
rw.Write(errOutput)
return
}
}
// Form is middleware to deserialize form-urlencoded data from the request.
// It gets data from the form-urlencoded body, if present, or from the
// query string. It uses the http.Request.ParseForm() method
// to perform deserialization, then reflection is used to map each field
// into the struct with the proper type. Structs with primitive slice types
// (bool, float, int, string) can support deserialization of repeated form
// keys, for example: key=val1&key=val2&key=val3
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func Form(req *http.Request, formStruct interface{}) Errors {
var errors Errors
ensurePointer(formStruct)
formStructV := reflect.ValueOf(formStruct)
parseErr := req.ParseForm()
// Format validation of the request body or the URL would add considerable overhead,
// and ParseForm does not complain when URL encoding is off.
// Because an empty request body or url can also mean absence of all needed values,
// it is not in all cases a bad request, so let's return 422.
if parseErr != nil {
errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error())
}
errors = mapForm(formStructV, req.Form, nil, errors)
return append(errors, Validate(req, formStruct)...)
}
// MaxMemory represents maximum amount of memory to use when parsing a multipart form.
// Set this to whatever value you prefer; default is 10 MB.
var MaxMemory = int64(1024 * 1024 * 10)
// MultipartForm works much like Form, except it can parse multipart forms
// and handle file uploads. Like the other deserialization middleware handlers,
// you can pass in an interface to make the interface available for injection
// into other handlers later.
func MultipartForm(req *http.Request, formStruct interface{}) Errors {
var errors Errors
ensurePointer(formStruct)
formStructV := reflect.ValueOf(formStruct)
// This if check is necessary due to https://github.com/martini-contrib/csrf/issues/6
if req.MultipartForm == nil {
// Workaround for multipart forms returning nil instead of an error
// when content is not multipart; see https://code.google.com/p/go/issues/detail?id=6334
if multipartReader, err := req.MultipartReader(); err != nil {
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
} else {
form, parseErr := multipartReader.ReadForm(MaxMemory)
if parseErr != nil {
errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error())
}
if req.Form == nil {
req.ParseForm()
}
for k, v := range form.Value {
req.Form[k] = append(req.Form[k], v...)
}
req.MultipartForm = form
}
}
errors = mapForm(formStructV, req.MultipartForm.Value, req.MultipartForm.File, errors)
return append(errors, Validate(req, formStruct)...)
}
// JSON is middleware to deserialize a JSON payload from the request
// into the struct that is passed in. The resulting struct is then
// validated, but no error handling is actually performed here.
// An interface pointer can be added as a second argument in order
// to map the struct to a specific interface.
func JSON(req *http.Request, jsonStruct interface{}) Errors {
var errors Errors
ensurePointer(jsonStruct)
if req.Body != nil {
defer req.Body.Close()
err := json.NewDecoder(req.Body).Decode(jsonStruct)
if err != nil && err != io.EOF {
errors.Add([]string{}, ERR_DESERIALIZATION, err.Error())
}
}
return append(errors, Validate(req, jsonStruct)...)
}
// RawValidate is same as Validate but does not require a HTTP context,
// and can be used independently just for validation.
// This function does not support Validator interface.
func RawValidate(obj interface{}) Errors {
var errs Errors
v := reflect.ValueOf(obj)
k := v.Kind()
if k == reflect.Interface || k == reflect.Ptr {
v = v.Elem()
k = v.Kind()
}
if k == reflect.Slice || k == reflect.Array {
for i := 0; i < v.Len(); i++ {
e := v.Index(i).Interface()
errs = validateStruct(errs, e)
}
} else {
errs = validateStruct(errs, obj)
}
return errs
}
// Validate is middleware to enforce required fields. If the struct
// passed in implements Validator, then the user-defined Validate method
// is executed, and its errors are mapped to the context. This middleware
// performs no error handling: it merely detects errors and maps them.
func Validate(req *http.Request, obj interface{}) Errors {
var errs Errors
v := reflect.ValueOf(obj)
k := v.Kind()
if k == reflect.Interface || k == reflect.Ptr {
v = v.Elem()
k = v.Kind()
}
if k == reflect.Slice || k == reflect.Array {
for i := 0; i < v.Len(); i++ {
e := v.Index(i).Interface()
errs = validateStruct(errs, e)
if validator, ok := e.(Validator); ok {
errs = validator.Validate(req, errs)
}
}
} else {
errs = validateStruct(errs, obj)
if validator, ok := obj.(Validator); ok {
errs = validator.Validate(req, errs)
}
}
return errs
}
var (
AlphaDashPattern = regexp.MustCompile(`[^\d\w-_]`)
AlphaDashDotPattern = regexp.MustCompile(`[^\d\w-_\.]`)
EmailPattern = regexp.MustCompile(`\A[\w!#$%&'*+/=?^_`+"`"+`{|}~-]+(?:\.[\w!#$%&'*+/=?^_`+"`"+`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?\.)+[a-zA-Z0-9](?:[\w-]*[\w])?\z`)
)
// Copied from github.com/asaskevich/govalidator.
const _MAX_URL_RUNE_COUNT = 2083
const _MIN_URL_RUNE_COUNT = 3
var (
urlSchemaRx = `((ftp|tcp|udp|wss?|https?):\/\/)`
urlUsernameRx = `(\S+(:\S*)?@)`
urlIPRx = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
ipRx = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
urlSubdomainRx = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
urlPortRx = `(:(\d{1,5}))`
urlPathRx = `((\/|\?|#)[^\s]*)`
URLPattern = regexp.MustCompile(`\A` + urlSchemaRx + `?` + urlUsernameRx + `?` + `((` + urlIPRx + `|(\[` + ipRx + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + urlSubdomainRx + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + urlPortRx + `?` + urlPathRx + `?\z`)
)
// IsURL check if the string is an URL.
func isURL(str string) bool {
if str == "" || utf8.RuneCountInString(str) >= _MAX_URL_RUNE_COUNT || len(str) <= _MIN_URL_RUNE_COUNT || strings.HasPrefix(str, ".") {
return false
}
u, err := url.Parse(str)
if err != nil {
return false
}
if strings.HasPrefix(u.Host, ".") {
return false
}
if u.Host == "" && (u.Path != "" && !strings.Contains(u.Path, ".")) {
return false
}
return URLPattern.MatchString(str)
}
type (
// Rule represents a validation rule.
Rule struct {
// IsMatch checks if rule matches.
IsMatch func(string) bool
// IsValid applies validation rule to condition.
IsValid func(Errors, string, interface{}) (bool, Errors)
}
// ParamRule does same thing as Rule but passes rule itself to IsValid method.
ParamRule struct {
// IsMatch checks if rule matches.
IsMatch func(string) bool
// IsValid applies validation rule to condition.
IsValid func(Errors, string, string, interface{}) (bool, Errors)
}
// RuleMapper and ParamRuleMapper represent validation rule mappers,
// it allwos users to add custom validation rules.
RuleMapper []*Rule
ParamRuleMapper []*ParamRule
)
var ruleMapper RuleMapper
var paramRuleMapper ParamRuleMapper
// AddRule adds new validation rule.
func AddRule(r *Rule) {
ruleMapper = append(ruleMapper, r)
}
// AddParamRule adds new validation rule.
func AddParamRule(r *ParamRule) {
paramRuleMapper = append(paramRuleMapper, r)
}
func in(fieldValue interface{}, arr string) bool {
val := fmt.Sprintf("%v", fieldValue)
vals := strings.Split(arr, ",")
isIn := false
for _, v := range vals {
if v == val {
isIn = true
break
}
}
return isIn
}
func parseFormName(raw, actual string) string {
if len(actual) > 0 {
return actual
}
return nameMapper(raw)
}
// Performs required field checking on a struct
func validateStruct(errors Errors, obj interface{}) Errors {
typ := reflect.TypeOf(obj)
val := reflect.ValueOf(obj)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
}
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// Allow ignored fields in the struct
if field.Tag.Get("form") == "-" || !val.Field(i).CanInterface() {
continue
}
fieldVal := val.Field(i)
fieldValue := fieldVal.Interface()
zero := reflect.Zero(field.Type).Interface()
// Validate nested and embedded structs (if pointer, only do so if not nil)
if field.Type.Kind() == reflect.Struct ||
(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue) &&
field.Type.Elem().Kind() == reflect.Struct) {
errors = validateStruct(errors, fieldValue)
}
errors = validateField(errors, zero, field, fieldVal, fieldValue)
}
return errors
}
// Don't pass in pointers to bind to. Can lead to bugs.
func ensureNotPointer(obj interface{}) {
if reflect.TypeOf(obj).Kind() == reflect.Ptr {
panic("Pointers are not accepted as binding models")
}
}
func validateField(errors Errors, zero interface{}, field reflect.StructField, fieldVal reflect.Value, fieldValue interface{}) Errors {
if fieldVal.Kind() == reflect.Slice {
for i := 0; i < fieldVal.Len(); i++ {
sliceVal := fieldVal.Index(i)
if sliceVal.Kind() == reflect.Ptr {
sliceVal = sliceVal.Elem()
}
sliceValue := sliceVal.Interface()
zero := reflect.Zero(sliceVal.Type()).Interface()
if sliceVal.Kind() == reflect.Struct ||
(sliceVal.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, sliceValue) &&
sliceVal.Elem().Kind() == reflect.Struct) {
errors = validateStruct(errors, sliceValue)
}
/* Apply validation rules to each item in a slice. ISSUE #3
else {
errors = validateField(errors, zero, field, sliceVal, sliceValue)
}*/
}
}
rules := strings.Split(field.Tag.Get("binding"), ";")
if reflect.DeepEqual(zero, fieldValue) {
for _, rule := range rules {
if rule == "Required" {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
break
}
if strings.HasPrefix(rule, "Default(") {
if fieldVal.CanSet() {
errors = setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors)
} else {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default")
}
break
}
}
return errors
}
VALIDATE_RULES:
for _, rule := range rules {
if len(rule) == 0 {
continue
}
switch {
case rule == "Required":
continue
case strings.HasPrefix(rule, "Default("):
continue
case rule == "OmitEmpty": // legacy
continue
case rule == "AlphaDash":
if AlphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash")
break VALIDATE_RULES
}
case rule == "AlphaDashDot":
if AlphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Size("):
size, _ := strconv.Atoi(rule[5 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) != size {
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() != size {
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MinSize("):
min, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MaxSize("):
max, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
if fieldVal.Kind() == reflect.Slice && fieldVal.Len() > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Range("):
nums := strings.Split(rule[6:len(rule)-1], ",")
if len(nums) != 2 {
break VALIDATE_RULES
}
val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt()
if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() {
errors.Add([]string{field.Name}, ERR_RANGE, "Range")
break VALIDATE_RULES
}
case rule == "Email":
if !EmailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_EMAIL, "Email")
break VALIDATE_RULES
}
case rule == "Url":
str := fmt.Sprintf("%v", fieldValue)
if !isURL(str) {
errors.Add([]string{field.Name}, ERR_URL, "Url")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "In("):
if !in(fieldValue, rule[3:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_IN, "In")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "NotIn("):
if in(fieldValue, rule[6:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Include("):
if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_INCLUDE, "Include")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Exclude("):
if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude")
break VALIDATE_RULES
}
default:
// Apply custom validation rules
var isValid bool
for i := range ruleMapper {
if ruleMapper[i].IsMatch(rule) {
isValid, errors = ruleMapper[i].IsValid(errors, field.Name, fieldValue)
if !isValid {
break VALIDATE_RULES
}
}
}
for i := range paramRuleMapper {
if paramRuleMapper[i].IsMatch(rule) {
isValid, errors = paramRuleMapper[i].IsValid(errors, rule, field.Name, fieldValue)
if !isValid {
break VALIDATE_RULES
}
}
}
}
}
return errors
}
// NameMapper represents a form tag name mapper.
type NameMapper func(string) string
var (
nameMapper = func(field string) string {
newstr := make([]rune, 0, len(field))
for i, chr := range field {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
chr -= ('A' - 'a')
}
newstr = append(newstr, chr)
}
return string(newstr)
}
)
// SetNameMapper sets name mapper.
func SetNameMapper(nm NameMapper) {
nameMapper = nm
}
// Takes values from the form data and puts them into a struct
func mapForm(formStruct reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) Errors {
if formStruct.Kind() == reflect.Ptr {
formStruct = formStruct.Elem()
}
typ := formStruct.Type()
for i := 0; i < typ.NumField(); i++ {
typeField := typ.Field(i)
structField := formStruct.Field(i)
if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous {
structField.Set(reflect.New(typeField.Type.Elem()))
errors = mapForm(structField.Elem(), form, formfile, errors)
if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) {
structField.Set(reflect.Zero(structField.Type()))
}
} else if typeField.Type.Kind() == reflect.Struct {
errors = mapForm(structField, form, formfile, errors)
}
inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form"))
if len(inputFieldName) == 0 || !structField.CanSet() {
continue
}
inputValue, exists := form[inputFieldName]
if exists {
numElems := len(inputValue)
if structField.Kind() == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
errors = setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
formStruct.Field(i).Set(slice)
} else {
errors = setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}
continue
}
inputFile, exists := formfile[inputFieldName]
if !exists {
continue
}
fhType := reflect.TypeOf((*multipart.FileHeader)(nil))
numElems := len(inputFile)
if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType {
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
slice.Index(i).Set(reflect.ValueOf(inputFile[i]))
}
structField.Set(slice)
} else if structField.Type() == fhType {
structField.Set(reflect.ValueOf(inputFile[0]))
}
}
return errors
}
// This sets the value in a struct of an indeterminate type to the
// matching value from the request (via Form middleware) in the
// same type, so that not all deserialized values have to be strings.
// Supported types are string, int, float, and bool.
func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value, nameInTag string, errors Errors) Errors {
switch valueKind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if val == "" {
val = "0"
}
intVal, err := strconv.ParseInt(val, 10, 64)
if err != nil {
errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as integer")
} else {
structField.SetInt(intVal)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if val == "" {
val = "0"
}
uintVal, err := strconv.ParseUint(val, 10, 64)
if err != nil {
errors.Add([]string{nameInTag}, ERR_INTERGER_TYPE, "Value could not be parsed as unsigned integer")
} else {
structField.SetUint(uintVal)
}
case reflect.Bool:
if val == "on" {
structField.SetBool(true)
break
}
if val == "" {
val = "false"
}
boolVal, err := strconv.ParseBool(val)
if err != nil {
errors.Add([]string{nameInTag}, ERR_BOOLEAN_TYPE, "Value could not be parsed as boolean")
} else if boolVal {
structField.SetBool(true)
}
case reflect.Float32:
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, 32)
if err != nil {
errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 32-bit float")
} else {
structField.SetFloat(floatVal)
}
case reflect.Float64:
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, 64)
if err != nil {
errors.Add([]string{nameInTag}, ERR_FLOAT_TYPE, "Value could not be parsed as 64-bit float")
} else {
structField.SetFloat(floatVal)
}
case reflect.String:
structField.SetString(val)
}
return errors
}
// Pointers must be bind to.
func ensurePointer(obj interface{}) {
if reflect.TypeOf(obj).Kind() != reflect.Ptr {
panic("Pointers are only accepted as binding models")
}
}
type (
// ErrorHandler is the interface that has custom error handling process.
ErrorHandler interface {
// Error handles validation errors with custom process.
Error(*http.Request, Errors)
}
// Validator is the interface that handles some rudimentary
// request validation logic so your application doesn't have to.
Validator interface {
// Validate validates that the request is OK. It is recommended
// that validation be limited to checking values for syntax and
// semantics, enough to know that you can make sense of the request
// in your application. For example, you might verify that a credit
// card number matches a valid pattern, but you probably wouldn't
// perform an actual credit card authorization here.
Validate(*http.Request, Errors) Errors
}
)