-
-
Save gorshkov-leonid/3d1493de1d99043046ab6a1304268f40 to your computer and use it in GitHub Desktop.
| package main | |
| import ( | |
| "fmt" | |
| "reflect" | |
| ) | |
| // Name of the struct tag used in examples | |
| const tagName = "validate" | |
| type User struct { | |
| Id int `validate:"-"` | |
| Name string `validate:"presence,min=2,max=32"` | |
| Email string `validate:"email,required"` | |
| } | |
| func main() { | |
| user := User{ | |
| Id: 1, | |
| Name: "John Doe", | |
| Email: "john@example", | |
| } | |
| // TypeOf returns the reflection Type that represents the dynamic type of variable. | |
| // If variable is a nil interface value, TypeOf returns nil. | |
| t := reflect.TypeOf(user) | |
| // Get the type and kind of our user variable | |
| fmt.Println("Type:", t.Name()) | |
| fmt.Println("Kind:", t.Kind()) | |
| // Iterate over all available fields and read the tag value | |
| for i := 0; i < t.NumField(); i++ { | |
| // Get the field, returns https://golang.org/pkg/reflect/#StructField | |
| field := t.Field(i) | |
| // Get the field tag value | |
| tag := field.Tag.Get(tagName) | |
| fmt.Printf("%d. %v (%v), tag: '%v'\n", i+1, field.Name, field.Type.Name(), tag) | |
| } | |
| } |
| package main | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "regexp" | |
| "strings" | |
| ) | |
| // Name of the struct tag used in examples. | |
| const tagName = "validate" | |
| // Regular expression to validate email address. | |
| var mailRe = regexp.MustCompile(`\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z`) | |
| // Generic data validator. | |
| type Validator interface { | |
| // Validate method performs validation and returns result and optional error. | |
| Validate(interface{}) (bool, error) | |
| } | |
| // DefaultValidator does not perform any validations. | |
| type DefaultValidator struct { | |
| } | |
| func (v DefaultValidator) Validate(val interface{}) (bool, error) { | |
| return true, nil | |
| } | |
| // StringValidator validates string presence and/or its length. | |
| type StringValidator struct { | |
| Min int | |
| Max int | |
| } | |
| func (v StringValidator) Validate(val interface{}) (bool, error) { | |
| l := len(val.(string)) | |
| if l == 0 { | |
| return false, fmt.Errorf("cannot be blank") | |
| } | |
| if l < v.Min { | |
| return false, fmt.Errorf("should be at least %v chars long", v.Min) | |
| } | |
| if v.Max >= v.Min && l > v.Max { | |
| return false, fmt.Errorf("should be less than %v chars long", v.Max) | |
| } | |
| return true, nil | |
| } | |
| // NumberValidator performs numerical value validation. | |
| // Its limited to int type for simplicity. | |
| type NumberValidator struct { | |
| Min int | |
| Max int | |
| } | |
| func (v NumberValidator) Validate(val interface{}) (bool, error) { | |
| num := val.(int) | |
| if num < v.Min { | |
| return false, fmt.Errorf("should be greater than %v", v.Min) | |
| } | |
| if v.Max >= v.Min && num > v.Max { | |
| return false, fmt.Errorf("should be less than %v", v.Max) | |
| } | |
| return true, nil | |
| } | |
| // EmailValidator checks if string is a valid email address. | |
| type EmailValidator struct { | |
| } | |
| func (v EmailValidator) Validate(val interface{}) (bool, error) { | |
| if !mailRe.MatchString(val.(string)) { | |
| return false, fmt.Errorf("is not a valid email address") | |
| } | |
| return true, nil | |
| } | |
| // Returns validator struct corresponding to validation type | |
| func getValidatorFromTag(tag string) Validator { | |
| args := strings.Split(tag, ",") | |
| switch args[0] { | |
| case "number": | |
| validator := NumberValidator{} | |
| fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max) | |
| return validator | |
| case "string": | |
| validator := StringValidator{} | |
| fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &validator.Min, &validator.Max) | |
| return validator | |
| case "email": | |
| return EmailValidator{} | |
| } | |
| return DefaultValidator{} | |
| } | |
| // Performs actual data validation using validator definitions on the struct | |
| func validateStruct(s interface{}) []error { | |
| errs := []error{} | |
| // ValueOf returns a Value representing the run-time data | |
| v := reflect.ValueOf(s) | |
| for i := 0; i < v.NumField(); i++ { | |
| // Get the field tag value | |
| tag := v.Type().Field(i).Tag.Get(tagName) | |
| // Skip if tag is not defined or ignored | |
| if tag == "" || tag == "-" { | |
| continue | |
| } | |
| // Get a validator that corresponds to a tag | |
| validator := getValidatorFromTag(tag) | |
| // Perform validation | |
| valid, err := validator.Validate(v.Field(i).Interface()) | |
| // Append error to results | |
| if !valid && err != nil { | |
| errs = append(errs, fmt.Errorf("%s %s", v.Type().Field(i).Name, err.Error())) | |
| } | |
| } | |
| return errs | |
| } | |
| type User struct { | |
| Id int `validate:"number,min=1,max=1000"` | |
| Name string `validate:"string,min=2,max=10"` | |
| Bio string `validate:"string"` | |
| Email string `validate:"email"` | |
| } | |
| func main() { | |
| user := User{ | |
| Id: 0, | |
| Name: "superlongstring", | |
| Bio: "", | |
| Email: "foobar", | |
| } | |
| fmt.Println("Errors:") | |
| for i, err := range validateStruct(user) { | |
| fmt.Printf("\t%d. %s\n", i+1, err.Error()) | |
| } | |
| } |
register in IDE https://www.jetbrains.com/go/guide/tips/custom-structure-tags/
errors: best practice
https://banzaicloud.com/blog/error-handling-go/ https://github.com/emperror/emperror
https://www.bacancytechnology.com/blog/golang-error-handling phylosofy?
https://go.dev/blog/error-handling-and-go for dummies
https://go.dev/blog/errors-are-values from author !
https://eli.thegreenplace.net/2018/on-the-uses-and-misuses-of-panics-in-go/ panic or not panic
https://evilmartians.com/chronicles/errors-in-go-from-denial-to-acceptance from denial to acceptance !
- https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling.md draft
- https://habr.com/ru/post/422049/ как пользоваться check in go 2
-
Также обратите внимание, что с точки зрения «пуриста» приведенные ниже примеры кода не представляют собой самый идиоматичный Go. Я не придумал это полностью сам: источником вдохновения послужил исходный код Gin , популярного веб- фреймворка во вселенной Go. В Gin, если во время обработки запроса возникает критическая ошибка, вы можете вызвать panic(err)внутри обработчика, и Gin восстановится под капотом, зарегистрирует сообщение об ошибке и вернет статус 500 пользователю. - https://github.com/pkg/errors
- https://github.com/hashicorp/go-multierror
- https://pkg.go.dev/golang.org/x/sync/errgroup
https://golang.org/doc/go1.17 see New warnings for Is, As and Unwrap methods
https://ru.wikipedia.org/wiki/Go#%D0%9E%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B0_%D0%BE%D1%88%D0%B8%D0%B1%D0%BE%D0%BA_%D0%B8_%D0%B8%D1%81%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D1%81%D0%B8%D1%82%D1%83%D0%B0%D1%86%D0%B8%D0%B9 wiki
https://stackoverflow.com/questions/28745648/global-recover-handler-for-golang-http-panic mux recover
https://www.jajaldoang.com/post/handle-panic-in-http-server-using-middleware-go/ recover in all frameworks
with code highlight https://sosedoff.com/2016/07/16/golang-struct-tags.html