fix get on json

添加get模式支持json 标签,主要用于proto自动生成文件
This commit is contained in:
xxj
2021-06-16 17:21:09 +08:00
parent 3e2868f90a
commit b58fb29a5f
9 changed files with 595 additions and 10 deletions

View File

@@ -24,10 +24,6 @@ import (
"google.golang.org/grpc/status"
)
func (b *_Base) parseReqResp(typ reflect.Type, isObj bool) (reflect.Type, reflect.Type) {
return nil, nil
}
// checkHandlerFunc Judge whether to match rules
func (b *_Base) checkHandlerFunc(typ reflect.Type, isObj bool) (int, bool) { // 判断是否匹配规则,返回参数个数
offset := 0
@@ -267,8 +263,8 @@ func (b *_Base) getCallObj3(tvl, obj reflect.Value, methodName string) (func(*gi
return
}
var returnValues []reflect.Value
returnValues = tvl.Call([]reflect.Value{obj, reflect.ValueOf(apiFun(c)), req})
// var returnValues []reflect.Value
returnValues := tvl.Call([]reflect.Value{obj, reflect.ValueOf(apiFun(c)), req})
if returnValues != nil {
bainfo.Resp = returnValues[0].Interface()
@@ -320,11 +316,14 @@ func (b *_Base) handErrorString(c *gin.Context, req reflect.Value, err error) {
msg := message.GetErrorMsg(message.ParameterInvalid)
msg.Error = fmt.Sprintf("req param : %v", strings.Join(fields, ";"))
c.JSON(http.StatusBadRequest, msg)
return
}
func (b *_Base) unmarshal(c *gin.Context, v interface{}) error {
return c.ShouldBind(v)
err := c.ShouldBind(v)
if err != nil || strings.EqualFold(c.Request.Method, "get") { // get 模式 补刀json
err = mapJson(v, c.Request.Form)
}
return err
}
func (b *_Base) parserStruct(req, resp *parmInfo, astPkg *ast.Package, modPkg, modFile string) (r, p *mydoc.StructInfo) {
@@ -424,9 +423,10 @@ func (b *_Base) parserComments(f *ast.FuncDecl, objName, objFunc string, imports
gc.Methods = strings.Split(methods, ",")
}
gcs = append(gcs, gc)
} else {
// return nil, errors.New("Router information is missing")
}
// else {
// return nil, errors.New("Router information is missing")
// }
} else if strings.HasPrefix(t, objFunc) { // find note
t = strings.TrimSpace(strings.TrimPrefix(t, objFunc))
note += t

388
form_mapping.go Normal file
View File

@@ -0,0 +1,388 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package ginrpc
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"time"
"github.com/xxjwxc/ginrpc/internal/bytesconv"
"github.com/xxjwxc/ginrpc/internal/json"
)
var errUnknownType = errors.New("unknown type")
func mapJson(ptr interface{}, form map[string][]string) error {
return mapFormByTag(ptr, form, "json")
}
var emptyField = reflect.StructField{}
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
// Check if ptr is a map
ptrVal := reflect.ValueOf(ptr)
var pointed interface{}
if ptrVal.Kind() == reflect.Ptr {
ptrVal = ptrVal.Elem()
pointed = ptrVal.Interface()
}
if ptrVal.Kind() == reflect.Map &&
ptrVal.Type().Key().Kind() == reflect.String {
if pointed != nil {
ptr = pointed
}
return setFormMap(ptr, form)
}
return mappingByPtr(ptr, formSource(form), tag)
}
// setter tries to set value on a walking by fields of a struct
type setter interface {
TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
}
type formSource map[string][]string
var _ setter = formSource(nil)
// TrySet tries to set a value by request's form source (like map[string][]string)
func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
return setByForm(value, field, form, tagValue, opt)
}
func mappingByPtr(ptr interface{}, setter setter, tag string) error {
_, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
return err
}
func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
if field.Tag.Get(tag) == "-" { // just ignoring this field
return false, nil
}
var vKind = value.Kind()
if vKind == reflect.Ptr {
var isNew bool
vPtr := value
if value.IsNil() {
isNew = true
vPtr = reflect.New(value.Type().Elem())
}
isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
if err != nil {
return false, err
}
if isNew && isSetted {
value.Set(vPtr)
}
return isSetted, nil
}
if vKind != reflect.Struct || !field.Anonymous {
ok, err := tryToSetValue(value, field, setter, tag)
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
if vKind == reflect.Struct {
tValue := value.Type()
var isSetted bool
for i := 0; i < value.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
ok, err := mapping(value.Field(i), tValue.Field(i), setter, tag)
if err != nil {
return false, err
}
isSetted = isSetted || ok
}
return isSetted, nil
}
return false, nil
}
type setOptions struct {
isDefaultExists bool
defaultValue string
}
func tryToSetValue(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
var tagValue string
var setOpt setOptions
tagValue = field.Tag.Get(tag)
tagValue, opts := head(tagValue, ",")
if tagValue == "" { // default value is FieldName
tagValue = field.Name
}
if tagValue == "" { // when field is "emptyField" variable
return false, nil
}
var opt string
for len(opts) > 0 {
opt, opts = head(opts, ",")
if k, v := head(opt, "="); k == "default" {
setOpt.isDefaultExists = true
setOpt.defaultValue = v
}
}
return setter.TrySet(value, field, tagValue, setOpt)
}
func setByForm(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt setOptions) (isSetted bool, err error) {
vs, ok := form[tagValue]
if !ok && !opt.isDefaultExists {
return false, nil
}
switch value.Kind() {
case reflect.Slice:
if !ok {
vs = []string{opt.defaultValue}
}
return true, setSlice(vs, value, field)
case reflect.Array:
if !ok {
vs = []string{opt.defaultValue}
}
if len(vs) != value.Len() {
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
}
return true, setArray(vs, value, field)
default:
var val string
if !ok {
val = opt.defaultValue
}
if len(vs) > 0 {
val = vs[0]
}
return true, setWithProperType(val, value, field)
}
}
func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
switch value.Kind() {
case reflect.Int:
return setIntField(val, 0, value)
case reflect.Int8:
return setIntField(val, 8, value)
case reflect.Int16:
return setIntField(val, 16, value)
case reflect.Int32:
return setIntField(val, 32, value)
case reflect.Int64:
switch value.Interface().(type) {
case time.Duration:
return setTimeDuration(val, value, field)
}
return setIntField(val, 64, value)
case reflect.Uint:
return setUintField(val, 0, value)
case reflect.Uint8:
return setUintField(val, 8, value)
case reflect.Uint16:
return setUintField(val, 16, value)
case reflect.Uint32:
return setUintField(val, 32, value)
case reflect.Uint64:
return setUintField(val, 64, value)
case reflect.Bool:
return setBoolField(val, value)
case reflect.Float32:
return setFloatField(val, 32, value)
case reflect.Float64:
return setFloatField(val, 64, value)
case reflect.String:
value.SetString(val)
case reflect.Struct:
switch value.Interface().(type) {
case time.Time:
return setTimeField(val, field, value)
}
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
case reflect.Map:
return json.Unmarshal(bytesconv.StringToBytes(val), value.Addr().Interface())
default:
return errUnknownType
}
return nil
}
func setIntField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0"
}
intVal, err := strconv.ParseInt(val, 10, bitSize)
if err == nil {
field.SetInt(intVal)
}
return err
}
func setUintField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0"
}
uintVal, err := strconv.ParseUint(val, 10, bitSize)
if err == nil {
field.SetUint(uintVal)
}
return err
}
func setBoolField(val string, field reflect.Value) error {
if val == "" {
val = "false"
}
boolVal, err := strconv.ParseBool(val)
if err == nil {
field.SetBool(boolVal)
}
return err
}
func setFloatField(val string, bitSize int, field reflect.Value) error {
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, bitSize)
if err == nil {
field.SetFloat(floatVal)
}
return err
}
func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
timeFormat := structField.Tag.Get("time_format")
if timeFormat == "" {
timeFormat = time.RFC3339
}
switch tf := strings.ToLower(timeFormat); tf {
case "unix", "unixnano":
tv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
d := time.Duration(1)
if tf == "unixnano" {
d = time.Second
}
t := time.Unix(tv/int64(d), tv%int64(d))
value.Set(reflect.ValueOf(t))
return nil
}
if val == "" {
value.Set(reflect.ValueOf(time.Time{}))
return nil
}
l := time.Local
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
l = time.UTC
}
if locTag := structField.Tag.Get("time_location"); locTag != "" {
loc, err := time.LoadLocation(locTag)
if err != nil {
return err
}
l = loc
}
t, err := time.ParseInLocation(timeFormat, val, l)
if err != nil {
return err
}
value.Set(reflect.ValueOf(t))
return nil
}
func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
for i, s := range vals {
err := setWithProperType(s, value.Index(i), field)
if err != nil {
return err
}
}
return nil
}
func setSlice(vals []string, value reflect.Value, field reflect.StructField) error {
slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
err := setArray(vals, slice, field)
if err != nil {
return err
}
value.Set(slice)
return nil
}
func setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
d, err := time.ParseDuration(val)
if err != nil {
return err
}
value.Set(reflect.ValueOf(d))
return nil
}
func head(str, sep string) (head string, tail string) {
idx := strings.Index(str, sep)
if idx < 0 {
return str, ""
}
return str[:idx], str[idx+len(sep):]
}
func setFormMap(ptr interface{}, form map[string][]string) error {
el := reflect.TypeOf(ptr).Elem()
if el.Kind() == reflect.Slice {
ptrMap, ok := ptr.(map[string][]string)
if !ok {
return errors.New("cannot convert to map slices of strings")
}
for k, v := range form {
ptrMap[k] = v
}
return nil
}
ptrMap, ok := ptr.(map[string]string)
if !ok {
return errors.New("cannot convert to map of strings")
}
for k, v := range form {
ptrMap[k] = v[len(v)-1] // pick last
}
return nil
}

2
go.mod
View File

@@ -5,6 +5,8 @@ go 1.12
require (
github.com/gin-gonic/gin v1.7.2
github.com/go-playground/validator/v10 v10.4.1
github.com/goccy/go-json v0.7.0
github.com/json-iterator/go v1.1.9
github.com/xxjwxc/public v0.0.0-20210604020721-3cc06f1e88c0
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea // indirect
google.golang.org/grpc v1.29.1

2
go.sum
View File

@@ -61,6 +61,8 @@ github.com/go-redis/redis/v8 v8.4.11/go.mod h1:d5yY/TlkQyYBSBHnXUmnf1OrHbyQere5J
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.7.0 h1:ZsGt0RuL2c4EP8X33Ij8caSlBrcFx7eYKjlKG4HOWf8=
github.com/goccy/go-json v0.7.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=

View File

@@ -0,0 +1,24 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package bytesconv
import (
"unsafe"
)
// StringToBytes converts string to byte slice without a memory allocation.
func StringToBytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(
&struct {
string
Cap int
}{s, len(s)},
))
}
// BytesToString converts byte slice to string without a memory allocation.
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}

View File

@@ -0,0 +1,99 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package bytesconv
import (
"bytes"
"math/rand"
"strings"
"testing"
"time"
)
var testString = "Albert Einstein: Logic will get you from A to B. Imagination will take you everywhere."
var testBytes = []byte(testString)
func rawBytesToStr(b []byte) string {
return string(b)
}
func rawStrToBytes(s string) []byte {
return []byte(s)
}
// go test -v
func TestBytesToString(t *testing.T) {
data := make([]byte, 1024)
for i := 0; i < 100; i++ {
rand.Read(data)
if rawBytesToStr(data) != BytesToString(data) {
t.Fatal("don't match")
}
}
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
const (
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var src = rand.NewSource(time.Now().UnixNano())
func RandStringBytesMaskImprSrcSB(n int) string {
sb := strings.Builder{}
sb.Grow(n)
// A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
cache, remain = src.Int63(), letterIdxMax
}
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
sb.WriteByte(letterBytes[idx])
i--
}
cache >>= letterIdxBits
remain--
}
return sb.String()
}
func TestStringToBytes(t *testing.T) {
for i := 0; i < 100; i++ {
s := RandStringBytesMaskImprSrcSB(64)
if !bytes.Equal(rawStrToBytes(s), StringToBytes(s)) {
t.Fatal("don't match")
}
}
}
// go test -v -run=none -bench=^BenchmarkBytesConv -benchmem=true
func BenchmarkBytesConvBytesToStrRaw(b *testing.B) {
for i := 0; i < b.N; i++ {
rawBytesToStr(testBytes)
}
}
func BenchmarkBytesConvBytesToStr(b *testing.B) {
for i := 0; i < b.N; i++ {
BytesToString(testBytes)
}
}
func BenchmarkBytesConvStrToBytesRaw(b *testing.B) {
for i := 0; i < b.N; i++ {
rawStrToBytes(testString)
}
}
func BenchmarkBytesConvStrToBytes(b *testing.B) {
for i := 0; i < b.N; i++ {
StringToBytes(testString)
}
}

23
internal/json/go_json.go Normal file
View File

@@ -0,0 +1,23 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build go_json
// +build go_json
package json
import json "github.com/goccy/go-json"
var (
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// Unmarshal is exported by gin/json package.
Unmarshal = json.Unmarshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

23
internal/json/json.go Normal file
View File

@@ -0,0 +1,23 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build !jsoniter && !go_json
// +build !jsoniter,!go_json
package json
import "encoding/json"
var (
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// Unmarshal is exported by gin/json package.
Unmarshal = json.Unmarshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)

24
internal/json/jsoniter.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2017 Bo-Yi Wu. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build jsoniter
// +build jsoniter
package json
import jsoniter "github.com/json-iterator/go"
var (
json = jsoniter.ConfigCompatibleWithStandardLibrary
// Marshal is exported by gin/json package.
Marshal = json.Marshal
// Unmarshal is exported by gin/json package.
Unmarshal = json.Unmarshal
// MarshalIndent is exported by gin/json package.
MarshalIndent = json.MarshalIndent
// NewDecoder is exported by gin/json package.
NewDecoder = json.NewDecoder
// NewEncoder is exported by gin/json package.
NewEncoder = json.NewEncoder
)