diff --git a/32-platform/platform/placeholder/name_form.html b/32-platform/platform/placeholder/name_form.html
new file mode 100644
index 0000000..d915c99
--- /dev/null
+++ b/32-platform/platform/placeholder/name_form.html
@@ -0,0 +1,15 @@
+{{ layout "layout.html" }}
+
+
\ No newline at end of file
diff --git a/32-platform/platform/placeholder/name_handler.go b/32-platform/platform/placeholder/name_handler.go
index 22862ad..4c17be9 100644
--- a/32-platform/platform/placeholder/name_handler.go
+++ b/32-platform/platform/placeholder/name_handler.go
@@ -5,6 +5,7 @@ import (
"platform/http/actionresults"
"platform/http/handing"
"platform/logging"
+ "platform/validation"
)
var names = []string{"Alice", "Bob", "charlie", "Dora"}
@@ -12,10 +13,11 @@ var names = []string{"Alice", "Bob", "charlie", "Dora"}
type NameHandler struct {
logging.Logger
handing.URLGenerator
+ validation.Validator
}
type NewName struct {
- Name string
+ Name string `validation:"required,min:3"`
InsertAtStart bool
}
@@ -35,8 +37,16 @@ func (n NameHandler) GetNames() actionresults.ActionResult {
return actionresults.NewTemplateAction("simple_message.html", names)
}
+func (n NameHandler) GetFOrm() actionresults.ActionResult {
+ postUrl, _ := n.URLGenerator.GenerateUrl(NameHandler.PostName)
+ return actionresults.NewTemplateAction("name_form.html", postUrl)
+}
+
func (n NameHandler) PostName(new NewName) actionresults.ActionResult {
n.Logger.Debugf("PostName method invoked with argument: %v", new)
+ if ok, errs := n.Validator.Validate(&new); !ok {
+ return actionresults.NewTemplateAction("validation_errors.html", errs)
+ }
if new.InsertAtStart {
names = append([]string{new.Name}, names...)
} else {
diff --git a/32-platform/platform/placeholder/validation_errors.html b/32-platform/platform/placeholder/validation_errors.html
new file mode 100644
index 0000000..ad720ad
--- /dev/null
+++ b/32-platform/platform/placeholder/validation_errors.html
@@ -0,0 +1,8 @@
+{{ layout "layout.html" }}
+
+Validation Errors
+
+ {{ range . }}
+ - {{ .FieldName }}:{{ .Error }}
+ {{ end }}
+
\ No newline at end of file
diff --git a/32-platform/platform/services/services_default.go b/32-platform/platform/services/services_default.go
index 3b4d434..3b14785 100644
--- a/32-platform/platform/services/services_default.go
+++ b/32-platform/platform/services/services_default.go
@@ -4,6 +4,7 @@ import (
"platform/config"
"platform/logging"
"platform/templates"
+ "platform/validation"
)
func RegisterDefaultService() {
@@ -39,4 +40,12 @@ func RegisterDefaultService() {
if err != nil {
panic(err)
}
+
+ // 注册验证服务
+ err = AddSingleton(func() validation.Validator {
+ return validation.NewDefaultValidator()
+ })
+ if err != nil {
+ panic(err)
+ }
}
diff --git a/32-platform/platform/validation/tag_validator.go b/32-platform/platform/validation/tag_validator.go
new file mode 100644
index 0000000..8a8164a
--- /dev/null
+++ b/32-platform/platform/validation/tag_validator.go
@@ -0,0 +1,62 @@
+package validation
+
+import (
+ "reflect"
+ "strings"
+)
+
+func NewDefaultValidator() Validator {
+ return NewValidator(DefaultValidators())
+}
+
+func NewValidator(validators map[string]ValidatorFunc) Validator {
+ return &TagValidator{
+ validators: validators,
+ }
+}
+
+type TagValidator struct {
+ validators map[string]ValidatorFunc
+}
+
+func (tv *TagValidator) Validate(data interface{}) (ok bool, errs []ValidatorError) {
+ errs = []ValidatorError{}
+
+ dataVal := reflect.ValueOf(data)
+ if dataVal.Kind() == reflect.Ptr {
+ dataVal = dataVal.Elem()
+ }
+ if dataVal.Kind() != reflect.Struct {
+ panic("Only structs can be validated")
+ }
+ for i := 0; i < dataVal.NumField(); i++ {
+ fieldType := dataVal.Type().Field(i)
+ validationTag, found := fieldType.Tag.Lookup("validate")
+ if !found {
+ continue
+ }
+ for _, v := range strings.Split(validationTag, ",") {
+ var name, arg string = "", ""
+ if strings.Contains(v, ":") {
+ nameAndArgs := strings.SplitN(v, ":", 2)
+ name = nameAndArgs[0]
+ arg = nameAndArgs[1]
+ } else {
+ name = v
+ }
+ if validator, ok := tv.validators[name]; ok {
+ valid, err := validator(fieldType.Name, dataVal.Field(i).Interface(), arg)
+ if !valid {
+ errs = append(errs, ValidatorError{
+ FieldName: fieldType.Name,
+ Error: err,
+ })
+ } else {
+ panic("Unknow validator:" + name)
+ }
+ }
+ }
+ }
+ ok = len(errs) == 0
+ return
+}
diff --git a/32-platform/platform/validation/validator.go b/32-platform/platform/validation/validator.go
new file mode 100644
index 0000000..88be384
--- /dev/null
+++ b/32-platform/platform/validation/validator.go
@@ -0,0 +1,56 @@
+package validation
+
+import (
+ "fmt"
+ "strconv"
+)
+
+type Validator interface {
+ Validate(data interface{}) (ok bool, errs []ValidatorError)
+}
+
+// ValidatorError 验证错误
+type ValidatorError struct {
+ FieldName string
+ Error error
+}
+type ValidatorFunc func(fieldName string, value interface{}, arg string) (bool, error)
+
+func DefaultValidators() map[string]ValidatorFunc {
+ return map[string]ValidatorFunc{
+ "required": required,
+ "min": minValue,
+ }
+}
+
+func required(fieldName string, value interface{}, arg string) (valid bool, err error) {
+ if str, ok := value.(string); ok {
+ valid = str != ""
+ err = fmt.Errorf("字段 %v 不能为空", fieldName)
+ return
+ } else {
+ err = fmt.Errorf("字段 %v 必须为字符串", fieldName)
+ }
+ return
+}
+
+func minValue(fieldName string, value interface{}, arg string) (valid bool, err error) {
+ minVal, err := strconv.Atoi(arg)
+ if err != nil {
+ err = fmt.Errorf("字段 %v 的配置参数 %v 必须为数字", fieldName, arg)
+ return
+ }
+ if iVal, iValOk := value.(int); iValOk {
+ valid = iVal >= minVal
+ err = fmt.Errorf("字段 %v 的值不能小于 %v", fieldName, minVal)
+ } else if fVal, fValOk := value.(float64); fValOk {
+ valid = fVal >= float64(minVal)
+ err = fmt.Errorf("字段 %v 的值不能小于 %v", fieldName, minVal)
+ } else if sVal, sValOk := value.(string); sValOk {
+ err = fmt.Errorf("字段 %v 的值长度不能小于 %v", fieldName, minVal)
+ valid = len(sVal) >= minVal
+ } else {
+ err = fmt.Errorf("字段 %v 的值类型必须为int,float,string", fieldName)
+ }
+ return
+}