一、自定义模版的使用
如果想对官网goctl
命名生成的项目结构改变的话,可以使用模版,自定义模版,然后生成自己想要的文件
1、使用命令将官方模版映射到本地
goctl template init
2、在项目的根目录下添加文件夹,把刚刚映射到本地的拷贝到项目中
3、使用模版根据api
文件来生成go
的文件
# 注意这个地方要根据你当前路径来找到goctl文件夹goctl api go -api *api --dir . --style=goZero --home ../../goctl
4、注意
goctl api go -api *.api -dir . --style=gozero
可以根据api
文件来生成项目,也是修改了api
来更新项目goctl api new user
是创建一个user
的api
项目 二、使用go-playground
对前端数据校验
1、安装依赖包
go get -u github.com/go-playground/validator/v10
2、在translator.go
文件中自定义将错误翻译成中文
package utilsimport ("errors""github.com/go-playground/locales/zh"ut "github.com/go-playground/universal-translator""github.com/go-playground/validator/v10"zhTranslations "github.com/go-playground/validator/v10/translations/zh""go_zero_demo05/common/utils/validators""reflect""strings")func Validate(dataStruct interface{}) error {zhT := zh.New()validate := validator.New()// 注册一个函数,获取struct tag里自定义的label作为字段名validate.RegisterTagNameFunc(func(fld reflect.StructField) string {name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]if name == "-" {return ""}return name})uni := ut.New(zhT)trans, _ := uni.GetTranslator("zh")// 注册自定义结构体字段校验方法// 1.注册时间要比当前的晚if err := validate.RegisterValidation("checkAfterDate", validators.ValidateAfterDate); err != nil {return err}if err := validate.RegisterTranslation("checkAfterDate",trans,registerTranslator("checkAfterDate", "{0} 必须要晚于当前日期"),translate,); err != nil {return err}// 验证器注册翻译器if err := zhTranslations.RegisterDefaultTranslations(validate, trans); err != nil {return err}err := validate.Struct(dataStruct)if err != nil {for _, err1 := range err.(validator.ValidationErrors) {return errors.New(err1.Translate(trans))}}return nil}// registerTranslator 为自定义字段添加翻译功能func registerTranslator(tag string, msg string) validator.RegisterTranslationsFunc {return func(trans ut.Translator) error {if err := trans.Add(tag, msg, false); err != nil {return err}return nil}}// translate 自定义字段的翻译方法func translate(trans ut.Translator, fe validator.FieldError) string {msg, err := trans.T(fe.Tag(), fe.Field())if err != nil {panic(fe.(error).Error())}return msg}
3、关于自定义校验方法
package validatorsimport ("github.com/go-playground/validator/v10""time")// ValidateAfterDate 判断时间是否是当前时间之后func ValidateAfterDate(fl validator.FieldLevel) bool {date, err := time.Parse("2006-01-02", fl.Field().String())if err != nil {return false}if date.Before(time.Now()) {return false}return true}
4、自定义模版handler.tpl
中添加前端参数校验的方法
package {{.PkgName}}import ("net/http""github.com/zeromicro/go-zero/rest/httpx"{{.ImportPackages}})func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {return func(w http.ResponseWriter, r *http.Request) {{{if .HasRequest}}var req types.{{.RequestType}}if err := httpx.Parse(r, &req); err != nil {httpx.ErrorCtx(r.Context(), w, err) return}// 数据验证 if validateErr := utils.Validate(&req);validateErr != nil { httpx.ErrorCtx(r.Context(), w, validateErr) return }{{end}}l := {{.LogicName}}.New{{.LogicType}}(r.Context(), svcCtx){{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})if err != nil { httpx.ErrorCtx(r.Context(), w, err) } else { {{if .HasResp}}httpx.OkJsonCtx(r.Context(), w, resp){{else}}httpx.Ok(w){{end}} }}}
5、在定义api
的时候加上参数校验
type PostDemoReq {Name string `json:"name" validate:"required"` // 姓名Age int64 `json:"age" validate:"required,gte=1,lte=130"` // 年龄// optional 表示可选,omitempty如果为空的时候不走后面Mobile string `json:"mobile,optional" validate:"omitempty,checkMobile"` // 手机号码Email string `json:"email,optional" validate:"omitempty,checkEmail"` // 邮箱地址Date string `json:"date" validate:"omitempty,checkDate,checkAfterDate"` // 时间Password string `json:"password" validate:"required"` // 密码ConfimPassword string `json:"confimPassword" validate:"eqfield=Password"` // 确认密码}
optional
表示go-zero
参数是可选,一个很大作用是生成的swagger
上没有标识必填字段
checkMobile
、checkEmail
、checkAfterDate
都是自定义校验器,需要在translator.go
注册才能使用
omitempty
表示如果为空的话,就不走后面的校验
6、使用命令用模版生成文件,注意导包的问题
goctl api go -api *api --dir . --style=goZero --home ../../goctl
7、自测错误,并且返回给前端
8、当go-zero
中标识必填字段的时候提示的错误为下面,比如name
字段不写的时候,age
字段数据类型错误的时候
9、age
数据类型错误的时候
三、翻译go-zero
中的参数错误
1、自定义translatorError.go
翻译文件
package utilsimport ("fmt""regexp""strings")func TranslatorError(err error) string {reg := regexp.MustCompile(`"(.*)?"`)match := reg.FindString(err.Error())if strings.Contains(err.Error(), "is not set") {if match != "" {return fmt.Sprintf("%s 为必填字段!", strings.Replace(match, `"`, "", -1))}} else if strings.Contains(err.Error(), "mismatch for field") {if match != "" {return fmt.Sprintf("%s 数据类型不对!", strings.Replace(match, `"`, "", -1))}}return ""}
2、在返回错误拦截器中添加使用
func ErrHandler(name string) func(ctx context.Context, err error) (int, any) {return func(ctx context.Context, err error) (int, any) {fmt.Println(err, "错误信息")causeErr := errors.Cause(err)fmt.Println(causeErr, "111-->")var errMessage = err.Error()// 翻译错误translatorError := TranslatorError(err)if translatorError != "" {errMessage = translatorError}// 日志记录logx.WithContext(ctx).Errorf("【%s】 err %v", name, errMessage)return http.StatusBadRequest, Fail(errMessage)}}
3、当年龄数据类型错误的时候
4、name
字段没写的时候
5、另外name
为空字符串的时候
四、参考文档
1、go-playground文档
2、go-zero官网