1 Star 0 Fork 0

ltotal / ppw_gin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
api.go 6.34 KB
一键复制 编辑 原始数据 按行查看 历史
ltotal 提交于 2024-04-29 11:06 . 底层优化
package pkg
import (
"fmt"
errs "gitee.com/ltotal/ppw_gin/errors"
"github.com/gin-gonic/gin"
"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"
zhTranslations "github.com/go-playground/validator/v10/translations/zh"
"github.com/pkg/errors"
"net/http"
"reflect"
"regexp"
"strings"
)
type debugResponse struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
Sqls []string `json:"sqls"`
Cause string `json:"cause"`
Reference string `json:"reference"`
Stack []string `json:"stack"`
}
type response struct {
Code int `json:"code"`
Data interface{} `json:"data"`
Msg string `json:"msg"`
}
type Rule struct {
Method string
Route string
Handler interface{}
DataBind binding.Binding
}
type Routes struct {
Group string
Rules []Rule
}
type Validators struct {
Tag string
RegExp string
Handler func(fl validator.FieldLevel) bool
Msg string
}
type ResponseContent struct {
Code int
Data interface{}
Error error
}
type ApiPkg struct {
validate *validator.Validate
uni *ut.UniversalTranslator
trans ut.Translator
}
var Api = &ApiPkg{}
func (p *ApiPkg) RegisterRoutes(r *gin.Engine, routes ...*Routes) {
//初始化错误翻译
p.validate = binding.Validator.Engine().(*validator.Validate)
newZh := zh.New()
p.uni = ut.New(newZh, newZh)
p.trans, _ = p.uni.GetTranslator("zh")
_ = zhTranslations.RegisterDefaultTranslations(p.validate, p.trans)
for _, route := range routes {
rules := route.Rules
group := r.Group(route.Group)
for _, rule := range rules {
group.Handle(rule.Method, rule.Route, p.bindHandler(rule.Handler, rule.DataBind))
}
}
}
func (p *ApiPkg) RegisterValidates(validates *[]Validators) {
for _, v := range *validates {
if v.RegExp != "" {
p.addRegValidator(v.Tag, v.RegExp, v.Msg)
} else if v.Handler != nil {
p.addFuncValidator(v.Tag, v.Handler, v.Msg)
}
}
}
func (p *ApiPkg) GetErrorResponse(data interface{}, sqls []string, err error) (int, interface{}) {
code := http.StatusOK
msg := ""
cause := ""
reference := ""
if sqls == nil {
sqls = []string{}
}
var stackSlice []string
var apiErr errs.ApiCoder = nil
if err != nil {
data = struct{}{}
apiErr = errs.AsApiCoder(err)
if apiErr != nil {
code = apiErr.Code()
msg = apiErr.Message()
cause = errs.Cause(err).Error()
reference = apiErr.Reference()
} else {
code = http.StatusBadRequest
msg = err.Error()
}
}
if gin.IsDebugging() {
stackErr := errors.WithStack(err)
stackSlice = make([]string, 0)
strStack := strings.TrimSpace(errs.StackTrace(stackErr))
if strStack != "" {
stackSlice = strings.Split(strStack, "\n")
for i, line := range stackSlice {
stackSlice[i] = strings.TrimSpace(line)
}
}
return http.StatusOK, debugResponse{
Code: code,
Data: data,
Msg: msg,
Sqls: sqls,
Cause: cause,
Reference: reference,
Stack: stackSlice,
}
} else {
return http.StatusOK, response{
Code: code,
Data: data,
Msg: msg,
}
}
}
func (p *ApiPkg) bindHandler(f interface{}, bind binding.Binding) gin.HandlerFunc {
t := reflect.TypeOf(f)
if t.Kind() != reflect.Func {
panic("处理器必须为方法")
}
fNumIn := t.NumIn()
if fNumIn != 2 {
panic("路由处理器需要2个请求参数")
} else {
tc := reflect.TypeOf(&gin.Context{})
if t.In(0) != tc {
panic("第1个请求参数必须为*gin.Context类型")
}
}
if t.NumOut() != 2 {
panic("返回参数数量有误")
}
errorInterface := reflect.TypeOf((*error)(nil)).Elem()
if !t.Out(1).Implements(errorInterface) {
panic("第2个返回参数需为error类型")
}
return func(c *gin.Context) {
GCM.SetContext(c)
var req interface{}
req = p.newReqInstance(t.In(1))
err := c.ShouldBindWith(req, bind)
if err != nil {
tErr := p.translate(err)
if len(tErr) < 1 {
re := regexp.MustCompile(`strconv\.Parse\w+: parsing "(\w+)"`)
matches := re.FindStringSubmatch(err.Error())
if len(matches) > 1 {
tErr = append(tErr, fmt.Sprintf("字段类型异常: %s", matches[1]))
} else {
tErr = append(tErr, err.Error())
}
}
resErr := "api校验异常"
if len(tErr) > 0 {
resErr = tErr[0]
}
c.JSON(http.StatusOK, response{
Code: http.StatusBadRequest,
Data: struct{}{},
Msg: resErr,
})
return
}
var inValues []reflect.Value
inValues = []reflect.Value{reflect.ValueOf(c), reflect.ValueOf(req)}
res := reflect.ValueOf(f).Call(inValues)
var resErr error = nil
if res[1].Interface() != nil {
errors.As(res[1].Interface().(error), &resErr)
}
sqlsRes, _ := c.Get("sqls")
sqls, ok := sqlsRes.([]string)
if !ok {
sqls = []string{}
}
c.JSON(p.GetErrorResponse(res[0].Interface(), sqls, resErr))
}
}
func (p *ApiPkg) addRegValidator(tagName string, regStr string, msg string) {
if err := p.validate.RegisterValidation(tagName, func(fl validator.FieldLevel) bool {
field := fl.Field()
if value, ok := field.Interface().(string); ok {
re := regexp.MustCompile(regStr)
return value != "" && re.MatchString(value)
}
return false
}); err != nil {
panic(err)
}
p.updateTranslation(tagName, msg)
}
func (p *ApiPkg) addFuncValidator(tagName string, validateFunc func(fl validator.FieldLevel) bool, msg string) {
if err := p.validate.RegisterValidation(tagName, validateFunc); err != nil {
panic(err)
}
p.updateTranslation(tagName, msg)
}
func (p *ApiPkg) translate(tErr error) []string {
var result []string
var validationErrs validator.ValidationErrors
if errors.As(tErr, &validationErrs) {
for _, err := range validationErrs {
result = append(result, err.Translate(p.trans))
}
}
return result
}
func (p *ApiPkg) updateTranslation(tagName string, msg string) {
if err := binding.Validator.Engine().(*validator.Validate).RegisterTranslation(tagName, p.trans, func(ut ut.Translator) error {
return ut.Add(tagName, msg, true)
}, func(ut ut.Translator, fe validator.FieldError) string {
t, _ := ut.T(fe.Tag(), fe.Field())
return t
}); err != nil {
panic(err)
}
}
func (p *ApiPkg) newReqInstance(t reflect.Type) interface{} {
switch t.Kind() {
case reflect.Ptr, reflect.Interface:
return p.newReqInstance(t.Elem())
default:
return reflect.New(t).Interface()
}
}
1
https://gitee.com/ltotal/ppw_gin.git
git@gitee.com:ltotal/ppw_gin.git
ltotal
ppw_gin
ppw_gin
v0.8.3

搜索帮助