mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-02 22:10:25 +08:00
refactor: 后端validator校验错误转译
This commit is contained in:
@@ -23,7 +23,7 @@
|
|||||||
"monaco-sql-languages": "^0.11.0",
|
"monaco-sql-languages": "^0.11.0",
|
||||||
"monaco-themes": "^0.4.4",
|
"monaco-themes": "^0.4.4",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.1.4",
|
"pinia": "^2.1.6",
|
||||||
"qrcode.vue": "^3.4.0",
|
"qrcode.vue": "^3.4.0",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"sortablejs": "^1.13.0",
|
"sortablejs": "^1.13.0",
|
||||||
|
|||||||
4
mayfly_go_web/src/common/pattern.ts
Normal file
4
mayfly_go_web/src/common/pattern.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const AccountUsernamePattern = {
|
||||||
|
pattern: /^[a-zA-Z0-9_]{5,20}$/g,
|
||||||
|
message: '只允许输入5-20位大小写字母、数字、下划线',
|
||||||
|
};
|
||||||
@@ -138,6 +138,7 @@ import { letterAvatar } from '@/common/utils/string';
|
|||||||
import { useUserInfo } from '@/store/userInfo';
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
import QrcodeVue from 'qrcode.vue';
|
import QrcodeVue from 'qrcode.vue';
|
||||||
import { personApi } from '@/views/personal/api';
|
import { personApi } from '@/views/personal/api';
|
||||||
|
import { AccountUsernamePattern } from '@/common/pattern';
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||||
@@ -205,7 +206,14 @@ const state = reactive({
|
|||||||
name: '',
|
name: '',
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
username: [
|
||||||
|
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||||
|
{
|
||||||
|
pattern: AccountUsernamePattern.pattern,
|
||||||
|
message: AccountUsernamePattern.message,
|
||||||
|
trigger: ['blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,11 +2,17 @@
|
|||||||
<div class="account-dialog">
|
<div class="account-dialog">
|
||||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%" :destroy-on-close="true">
|
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%" :destroy-on-close="true">
|
||||||
<el-form :model="form" ref="accountForm" :rules="rules" label-width="auto">
|
<el-form :model="form" ref="accountForm" :rules="rules" label-width="auto">
|
||||||
<el-form-item prop="name" label="姓名:" required>
|
<el-form-item prop="name" label="姓名:">
|
||||||
<el-input v-model.trim="form.name" placeholder="请输入姓名" auto-complete="off"></el-input>
|
<el-input v-model.trim="form.name" placeholder="请输入姓名" auto-complete="off" clearable></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item prop="username" label="用户名:" required>
|
<el-form-item prop="username" label="用户名:">
|
||||||
<el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名,密码默认与账号名一致" auto-complete="off"></el-input>
|
<el-input
|
||||||
|
:disabled="edit"
|
||||||
|
v-model.trim="form.username"
|
||||||
|
placeholder="请输入账号用户名,密码默认与账号名一致"
|
||||||
|
auto-complete="off"
|
||||||
|
clearable
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="edit" prop="password" label="密码:">
|
<el-form-item v-if="edit" prop="password" label="密码:">
|
||||||
<el-input type="password" v-model.trim="form.password" placeholder="输入密码可修改用户密码" autocomplete="new-password"></el-input>
|
<el-input type="password" v-model.trim="form.password" placeholder="输入密码可修改用户密码" autocomplete="new-password"></el-input>
|
||||||
@@ -27,6 +33,7 @@
|
|||||||
import { toRefs, reactive, watch, ref } from 'vue';
|
import { toRefs, reactive, watch, ref } from 'vue';
|
||||||
import { accountApi } from '../api';
|
import { accountApi } from '../api';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { AccountUsernamePattern } from '@/common/pattern';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -59,6 +66,11 @@ const rules = {
|
|||||||
message: '请输入用户名',
|
message: '请输入用户名',
|
||||||
trigger: ['change', 'blur'],
|
trigger: ['change', 'blur'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
pattern: AccountUsernamePattern.pattern,
|
||||||
|
message: AccountUsernamePattern.message,
|
||||||
|
trigger: ['blur'],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1558,10 +1558,10 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3:
|
|||||||
resolved "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz?cache=0&sync_timestamp=1621648246651&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpicomatch%2Fdownload%2Fpicomatch-2.3.0.tgz"
|
resolved "https://registry.nlark.com/picomatch/download/picomatch-2.3.0.tgz?cache=0&sync_timestamp=1621648246651&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fpicomatch%2Fdownload%2Fpicomatch-2.3.0.tgz"
|
||||||
integrity sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=
|
integrity sha1-8fBh3o9qS/AiiS4tEoI0+5gwKXI=
|
||||||
|
|
||||||
pinia@^2.1.4:
|
pinia@^2.1.6:
|
||||||
version "2.1.4"
|
version "2.1.6"
|
||||||
resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.4.tgz#a642adfe6208e10c36d3dc16184a91064788142a"
|
resolved "https://registry.npmmirror.com/pinia/-/pinia-2.1.6.tgz#e88959f14b61c4debd9c42d0c9944e2875cbe0fa"
|
||||||
integrity sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==
|
integrity sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vue/devtools-api" "^6.5.0"
|
"@vue/devtools-api" "^6.5.0"
|
||||||
vue-demi ">=0.14.5"
|
vue-demi ">=0.14.5"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import machineInit "mayfly-go/internal/machine/initialize"
|
import machineInit "mayfly-go/internal/machine/init"
|
||||||
|
|
||||||
func InitOther() {
|
func InitOther() {
|
||||||
machineInit.Init()
|
machineInit.Init()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package initialize
|
package init
|
||||||
|
|
||||||
import "mayfly-go/internal/machine/application"
|
import "mayfly-go/internal/machine/application"
|
||||||
|
|
||||||
@@ -2,14 +2,14 @@ package form
|
|||||||
|
|
||||||
type AccountCreateForm struct {
|
type AccountCreateForm struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Name string `json:"name" binding:"required"`
|
Name string `json:"name" binding:"required,max=16"`
|
||||||
Username string `json:"username" binding:"required,min=4,max=16"`
|
Username string `json:"username" binding:"pattern=account_username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountUpdateForm struct {
|
type AccountUpdateForm struct {
|
||||||
Name string `json:"name" binding:"max=16"` // 姓名
|
Name string `json:"name" binding:"max=16"` // 姓名
|
||||||
Username string `json:"username" binding:"max=20"`
|
Username string `json:"username" binding:"omitempty,pattern=account_username"`
|
||||||
Password *string `json:"password"`
|
Password *string `json:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,17 +6,19 @@ import (
|
|||||||
"mayfly-go/pkg/global"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils/structx"
|
"mayfly-go/pkg/utils/structx"
|
||||||
|
"mayfly-go/pkg/validatorx"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 绑定并校验请求结构体参数
|
// 绑定并校验请求结构体参数
|
||||||
func BindJsonAndValid[T any](g *gin.Context, data T) T {
|
func BindJsonAndValid[T any](g *gin.Context, data T) T {
|
||||||
if err := g.ShouldBindJSON(data); err != nil {
|
if err := g.ShouldBindJSON(data); err != nil {
|
||||||
panic(biz.NewBizErr(err.Error()))
|
panic(ConvBindValidationError(data, err))
|
||||||
} else {
|
} else {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@@ -32,7 +34,7 @@ func BindJsonAndCopyTo[T any](g *gin.Context, form any, toStruct T) T {
|
|||||||
// 绑定查询字符串到指定结构体
|
// 绑定查询字符串到指定结构体
|
||||||
func BindQuery[T any](g *gin.Context, data T) T {
|
func BindQuery[T any](g *gin.Context, data T) T {
|
||||||
if err := g.BindQuery(data); err != nil {
|
if err := g.BindQuery(data); err != nil {
|
||||||
panic(biz.NewBizErr(err.Error()))
|
panic(ConvBindValidationError(data, err))
|
||||||
} else {
|
} else {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@@ -41,7 +43,7 @@ func BindQuery[T any](g *gin.Context, data T) T {
|
|||||||
// 绑定查询字符串到指定结构体,并将分页信息也返回
|
// 绑定查询字符串到指定结构体,并将分页信息也返回
|
||||||
func BindQueryAndPage[T any](g *gin.Context, data T) (T, *model.PageParam) {
|
func BindQueryAndPage[T any](g *gin.Context, data T) (T, *model.PageParam) {
|
||||||
if err := g.BindQuery(data); err != nil {
|
if err := g.BindQuery(data); err != nil {
|
||||||
panic(biz.NewBizErr(err.Error()))
|
panic(ConvBindValidationError(data, err))
|
||||||
} else {
|
} else {
|
||||||
return data, GetPageParam(g)
|
return data, GetPageParam(g)
|
||||||
}
|
}
|
||||||
@@ -111,3 +113,11 @@ func ErrorRes(g *gin.Context, err any) {
|
|||||||
global.Log.Error(t)
|
global.Log.Error(t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 转换参数校验错误为业务异常错误
|
||||||
|
func ConvBindValidationError(data any, err error) error {
|
||||||
|
if e, ok := err.(validator.ValidationErrors); ok {
|
||||||
|
return biz.NewBizErrCode(403, validatorx.Translate2Str(data, e))
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,16 +11,15 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Log = logrus.New()
|
|
||||||
|
|
||||||
func Init() {
|
func Init() {
|
||||||
Log.SetFormatter(new(LogFormatter))
|
logger := logrus.New()
|
||||||
Log.SetReportCaller(true)
|
logger.SetFormatter(new(LogFormatter))
|
||||||
|
logger.SetReportCaller(true)
|
||||||
|
|
||||||
logConf := config.Conf.Log
|
logConf := config.Conf.Log
|
||||||
// 如果不存在日志配置信息,则默认debug级别
|
// 如果不存在日志配置信息,则默认debug级别
|
||||||
if logConf == nil {
|
if logConf == nil {
|
||||||
Log.SetLevel(logrus.DebugLevel)
|
logger.SetLevel(logrus.DebugLevel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +29,9 @@ func Init() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("日志级别不存在: %s", level))
|
panic(fmt.Sprintf("日志级别不存在: %s", level))
|
||||||
}
|
}
|
||||||
Log.SetLevel(l)
|
logger.SetLevel(l)
|
||||||
} else {
|
} else {
|
||||||
Log.SetLevel(logrus.DebugLevel)
|
logger.SetLevel(logrus.DebugLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
if logFile := logConf.File; logFile != nil {
|
if logFile := logConf.File; logFile != nil {
|
||||||
@@ -42,10 +41,10 @@ func Init() {
|
|||||||
panic(fmt.Sprintf("创建日志文件失败: %s", err.Error()))
|
panic(fmt.Sprintf("创建日志文件失败: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Out = file
|
logger.Out = file
|
||||||
}
|
}
|
||||||
|
|
||||||
global.Log = Log
|
global.Log = logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogFormatter struct{}
|
type LogFormatter struct{}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package req
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/logger"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/stringx"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
|
|
||||||
@@ -63,10 +63,10 @@ func LogHandler(rc *Ctx) error {
|
|||||||
go saveLog(rc)
|
go saveLog(rc)
|
||||||
}
|
}
|
||||||
if err := rc.Err; err != nil {
|
if err := rc.Err; err != nil {
|
||||||
logger.Log.WithFields(lfs).Error(getErrMsg(rc, err))
|
global.Log.WithFields(lfs).Error(getErrMsg(rc, err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
logger.Log.WithFields(lfs).Info(getLogMsg(rc))
|
global.Log.WithFields(lfs).Info(getLogMsg(rc))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,18 @@ import (
|
|||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 初始化jwt key与expire time等
|
||||||
func InitTokenConfig() {
|
func InitTokenConfig() {
|
||||||
|
if ExpTime == 0 {
|
||||||
JwtKey = config.Conf.Jwt.Key
|
JwtKey = config.Conf.Jwt.Key
|
||||||
ExpTime = config.Conf.Jwt.ExpireTime
|
ExpTime = config.Conf.Jwt.ExpireTime
|
||||||
|
|
||||||
|
// 如果配置文件中的jwt key为空,则随机生成字符串
|
||||||
|
if JwtKey == "" {
|
||||||
|
JwtKey = stringx.Rand(32)
|
||||||
|
global.Log.Infof("config.yml未配置jwt.key, 随机生成key为: %s", JwtKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -25,6 +34,8 @@ var (
|
|||||||
|
|
||||||
// 创建用户token
|
// 创建用户token
|
||||||
func CreateToken(userId uint64, username string) string {
|
func CreateToken(userId uint64, username string) string {
|
||||||
|
InitTokenConfig()
|
||||||
|
|
||||||
// 带权限创建令牌
|
// 带权限创建令牌
|
||||||
// 设置有效期,过期需要重新登录获取token
|
// 设置有效期,过期需要重新登录获取token
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
@@ -33,14 +44,9 @@ func CreateToken(userId uint64, username string) string {
|
|||||||
"exp": time.Now().Add(time.Minute * time.Duration(ExpTime)).Unix(),
|
"exp": time.Now().Add(time.Minute * time.Duration(ExpTime)).Unix(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// 如果配置文件中的jwt key为空,则随机生成字符串
|
|
||||||
if JwtKey == "" {
|
|
||||||
JwtKey = stringx.Rand(32)
|
|
||||||
global.Log.Infof("config.yml未配置jwt.key, 随机生成key为: %s", JwtKey)
|
|
||||||
}
|
|
||||||
// 使用自定义字符串加密 and get the complete encoded token as a string
|
// 使用自定义字符串加密 and get the complete encoded token as a string
|
||||||
tokenString, err := token.SignedString([]byte(JwtKey))
|
tokenString, err := token.SignedString([]byte(JwtKey))
|
||||||
biz.ErrIsNil(err, "token创建失败")
|
biz.ErrIsNilAppendErr(err, "token创建失败: %s")
|
||||||
return tokenString
|
return tokenString
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,6 +55,8 @@ func ParseToken(tokenStr string) (*model.LoginAccount, error) {
|
|||||||
if tokenStr == "" {
|
if tokenStr == "" {
|
||||||
return nil, errors.New("token error")
|
return nil, errors.New("token error")
|
||||||
}
|
}
|
||||||
|
InitTokenConfig()
|
||||||
|
|
||||||
// Parse token
|
// Parse token
|
||||||
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (any, error) {
|
token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (any, error) {
|
||||||
return []byte(JwtKey), nil
|
return []byte(JwtKey), nil
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"mayfly-go/pkg/config"
|
"mayfly-go/pkg/config"
|
||||||
"mayfly-go/pkg/global"
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/logger"
|
"mayfly-go/pkg/logger"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/validatorx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func RunWebServer() {
|
func RunWebServer() {
|
||||||
@@ -16,9 +16,6 @@ func RunWebServer() {
|
|||||||
// 初始化日志配置信息
|
// 初始化日志配置信息
|
||||||
logger.Init()
|
logger.Init()
|
||||||
|
|
||||||
// 初始化jwt key与expire time等
|
|
||||||
req.InitTokenConfig()
|
|
||||||
|
|
||||||
// 打印banner
|
// 打印banner
|
||||||
printBanner()
|
printBanner()
|
||||||
|
|
||||||
@@ -33,6 +30,9 @@ func RunWebServer() {
|
|||||||
global.Log.Fatalf("数据库升级失败: %v", err)
|
global.Log.Fatalf("数据库升级失败: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 参数校验器初始化、如错误提示中文转译等
|
||||||
|
validatorx.Init()
|
||||||
|
|
||||||
// 初始化其他需要启动时运行的方法
|
// 初始化其他需要启动时运行的方法
|
||||||
initialize.InitOther()
|
initialize.InitOther()
|
||||||
|
|
||||||
|
|||||||
41
server/pkg/validatorx/pattern.go
Normal file
41
server/pkg/validatorx/pattern.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package validatorx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
|
const CustomPatternTagName = "pattern"
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexpMap map[string]*regexp.Regexp
|
||||||
|
patternErrMsg map[string]string
|
||||||
|
)
|
||||||
|
|
||||||
|
// 注册自定义正则表达式校验规则
|
||||||
|
func RegisterCustomPatterns() {
|
||||||
|
// 账号用户名校验
|
||||||
|
RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册自定义正则表达式
|
||||||
|
func RegisterPattern(patternName string, regexpStr string, errMsg string) {
|
||||||
|
if regexpMap == nil {
|
||||||
|
regexpMap = make(map[string]*regexp.Regexp, 0)
|
||||||
|
patternErrMsg = make(map[string]string)
|
||||||
|
}
|
||||||
|
regexpMap[patternName] = regexp.MustCompile(regexpStr)
|
||||||
|
patternErrMsg[patternName] = errMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func patternValidFunc(f validator.FieldLevel) bool {
|
||||||
|
reg := regexpMap[f.Param()]
|
||||||
|
if reg == nil {
|
||||||
|
global.Log.Warnf("%s的正则校验规则不存在!", f.Param())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg.MatchString(f.Field().String())
|
||||||
|
}
|
||||||
92
server/pkg/validatorx/validatorx.go
Normal file
92
server/pkg/validatorx/validatorx.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
package validatorx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/pkg/utils/structx"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/go-playground/locales/zh"
|
||||||
|
ut "github.com/go-playground/universal-translator"
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
zh_trans "github.com/go-playground/validator/v10/translations/zh"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
trans ut.Translator
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
// 获取gin的校验器
|
||||||
|
validate, ok := binding.Validator.Engine().(*validator.Validate)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改返回字段key的格式
|
||||||
|
validate.RegisterTagNameFunc(func(fld reflect.StructField) string {
|
||||||
|
// 如果存在校验错误提示消息,则使用字段名,后续需要通过该字段名获取相应错误消息
|
||||||
|
if _, ok := fld.Tag.Lookup("valid_msg"); ok {
|
||||||
|
return fld.Name
|
||||||
|
}
|
||||||
|
name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
|
||||||
|
if name == "-" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
})
|
||||||
|
|
||||||
|
// 注册翻译器
|
||||||
|
zh := zh.New()
|
||||||
|
uni := ut.New(zh, zh)
|
||||||
|
|
||||||
|
trans, _ = uni.GetTranslator("zh")
|
||||||
|
|
||||||
|
// 注册翻译器
|
||||||
|
zh_trans.RegisterDefaultTranslations(validate, trans)
|
||||||
|
|
||||||
|
// 注册自定义校验器
|
||||||
|
validate.RegisterValidation(CustomPatternTagName, patternValidFunc)
|
||||||
|
|
||||||
|
// 注册自定义正则校验规则
|
||||||
|
RegisterCustomPatterns()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate 翻译错误信息
|
||||||
|
func Translate(data any, err error) map[string][]string {
|
||||||
|
var result = make(map[string][]string)
|
||||||
|
|
||||||
|
errors := err.(validator.ValidationErrors)
|
||||||
|
|
||||||
|
for _, err := range errors {
|
||||||
|
fieldName := err.Field()
|
||||||
|
|
||||||
|
// 判断该字段是否设置了自定义的错误描述信息,存在则使用自定义错误信息进行提示
|
||||||
|
if field, ok := structx.IndirectType(reflect.TypeOf(data)).FieldByName(fieldName); ok {
|
||||||
|
if errMsg, ok := field.Tag.Lookup("valid_msg"); ok {
|
||||||
|
result[fieldName] = append(result[fieldName], errMsg)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果是自定义正则校验规则,则使用自定义的错误描述信息
|
||||||
|
if err.Tag() == CustomPatternTagName {
|
||||||
|
result[fieldName] = append(result[fieldName], fieldName+patternErrMsg[err.Param()])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
result[fieldName] = append(result[fieldName], err.Translate(trans))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate 翻译错误信息为字符串
|
||||||
|
func Translate2Str(data any, err error) string {
|
||||||
|
res := Translate(data, err)
|
||||||
|
errMsgs := make([]string, 0)
|
||||||
|
for _, v := range res {
|
||||||
|
errMsgs = append(errMsgs, v...)
|
||||||
|
}
|
||||||
|
return strings.Join(errMsgs, ", ")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user