github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/courier/transport_http/transform/parameter_meta.go (about)

     1  package transform
     2  
     3  import (
     4  	"encoding"
     5  	"encoding/json"
     6  	"io"
     7  	"io/ioutil"
     8  	"reflect"
     9  
    10  	"github.com/johnnyeven/libtools/courier/status_error"
    11  	"github.com/johnnyeven/libtools/reflectx"
    12  	"github.com/johnnyeven/libtools/strutil"
    13  )
    14  
    15  func NewParameterMeta(field *reflect.StructField, rv reflect.Value, tagIn string, tagInFlags TagFlags) *ParameterMeta {
    16  	name, nameFlags := GetParameterDisplayName(field)
    17  
    18  	p := &ParameterMeta{
    19  		Name:    name,
    20  		In:      tagIn,
    21  		InFlags: tagInFlags,
    22  		Field:   field,
    23  		Value:   rv,
    24  	}
    25  
    26  	style, _, styleFlags := GetTagStyle(field)
    27  	p.Style = style
    28  	p.StyleFlags = styleFlags
    29  
    30  	if p.Style == "" {
    31  		switch p.In {
    32  		case "query", "cookie":
    33  			p.Style = "form"
    34  			// todo set explode as default
    35  		case "path", "header":
    36  			p.Style = "simple"
    37  		}
    38  	}
    39  
    40  	p.Format, _, _ = GetTagFmt(field)
    41  	if p.Format == "" {
    42  		p.Format = "json"
    43  	}
    44  
    45  	defaultValue, hasDefaultTag := GetTagDefault(field)
    46  	p.DefaultValue = defaultValue
    47  
    48  	p.Required = true
    49  	if hasOmitempty, ok := nameFlags["omitempty"]; ok {
    50  		p.Required = !hasOmitempty
    51  	} else {
    52  		// todo don't use non-default as required
    53  		p.Required = !hasDefaultTag
    54  	}
    55  
    56  	tagValidate, _ := GetTagValidate(field)
    57  	p.TagValidate = tagValidate
    58  
    59  	tagErrMsg, _ := GetTagErrMsg(field)
    60  	p.TagErrMsg = tagErrMsg
    61  
    62  	return p
    63  }
    64  
    65  type ParameterMeta struct {
    66  	Name   string
    67  	In     string
    68  	Format string
    69  
    70  	InFlags TagFlags
    71  	Field   *reflect.StructField
    72  	Value   reflect.Value
    73  
    74  	Style      string
    75  	StyleFlags TagFlags
    76  
    77  	DefaultValue string
    78  	Required     bool
    79  
    80  	TagValidate string
    81  	TagErrMsg   string
    82  }
    83  
    84  func (p *ParameterMeta) IsExplode() bool {
    85  	return p.StyleFlags != nil && p.StyleFlags["explode"]
    86  }
    87  
    88  func (p *ParameterMeta) IsMultipart() bool {
    89  	return p.InFlags != nil && p.InFlags["multipart"]
    90  }
    91  
    92  // for parameter
    93  func (p *ParameterMeta) UnmarshalStringAndValidate(ss ...string) error {
    94  	return p.UnmarshalAndValidate(BytesList(ResolveCommaSplitValues(p.Field.Type, ss...)...)...)
    95  }
    96  
    97  func (p *ParameterMeta) UnmarshalAndValidate(dataList ...[]byte) error {
    98  	if err := p.Unmarshal(dataList...); err != nil {
    99  		if statusErr, ok := err.(*status_error.StatusError); ok {
   100  			return statusErr
   101  		}
   102  		return status_error.InvalidField.StatusError().WithErrorField(p.In, p.Name, err.Error())
   103  	}
   104  	if valid, errFields := p.Validate(); !valid {
   105  		return status_error.InvalidField.StatusError().WithErrorFields(errFields...)
   106  	}
   107  	return nil
   108  }
   109  
   110  // for body
   111  func (p *ParameterMeta) UnmarshalFromReader(reader io.Reader) error {
   112  	if reader == nil {
   113  		return status_error.ReadFailed.StatusError()
   114  	}
   115  	data, readErr := ioutil.ReadAll(reader)
   116  	if readErr != nil {
   117  		return status_error.ReadFailed.StatusError()
   118  	}
   119  	if len(data) == 0 {
   120  		return status_error.InvalidBodyStruct.StatusError().WithDesc("empty body")
   121  	}
   122  	return p.UnmarshalAndValidate(data)
   123  }
   124  
   125  func (p *ParameterMeta) Unmarshal(datalist ...[]byte) error {
   126  	return ContentUnmarshal(p.Value, p.Field.Type, p.In, p.Name, p.Format, datalist...)
   127  }
   128  
   129  func (p *ParameterMeta) Marshal() (dataList [][]byte, err error) {
   130  	return ContentMarshal(p.Value, p.In, p.Format)
   131  }
   132  
   133  func (p *ParameterMeta) Validate() (bool, status_error.ErrorFields) {
   134  	errMsgMap := ErrMsgMap{}
   135  	parentField := ""
   136  
   137  	if reflectx.IndirectType(p.Field.Type).Kind() == reflect.Struct {
   138  		validateScan := NewScanner()
   139  		isValid, errMsgs := validateScan.Validate(p.Value, p.Field.Type)
   140  		if !isValid {
   141  			errMsgMap = errMsgMap.Merge(errMsgs)
   142  		}
   143  		if p.In == "formData" {
   144  			parentField = p.Name
   145  		}
   146  	} else {
   147  		errMsg := MarshalAndValidate(
   148  			p.Value, p.Field.Type,
   149  			p.DefaultValue, p.Required,
   150  			p.TagValidate, p.TagErrMsg,
   151  		)
   152  
   153  		if errMsg != "" {
   154  			errMsgMap[p.Name] = errMsg
   155  		}
   156  	}
   157  
   158  	return len(errMsgMap) == 0, errMsgMap.ErrorFieldsIn(p.In, parentField)
   159  }
   160  
   161  func BytesList(ss ...string) (dataList [][]byte) {
   162  	for _, s := range ss {
   163  		dataList = append(dataList, []byte(s))
   164  	}
   165  	return
   166  }
   167  
   168  func ContentMarshal(rv reflect.Value, in string, format string) (dataList [][]byte, err error) {
   169  	if rv.Kind() == reflect.Ptr && rv.IsNil() {
   170  		return
   171  	}
   172  
   173  	rv = reflect.Indirect(rv)
   174  
   175  	if marshal, ok := rv.Interface().(encoding.TextMarshaler); ok {
   176  		data, marshalErr := marshal.MarshalText()
   177  		if marshalErr != nil {
   178  			err = marshalErr
   179  			return
   180  		}
   181  		dataList = [][]byte{data}
   182  		return
   183  	}
   184  
   185  	switch rv.Kind() {
   186  	case reflect.Map, reflect.Struct:
   187  		data, errForMarshal := GetContentTransformer(format).Marshal(rv.Interface())
   188  		if errForMarshal != nil {
   189  			err = errForMarshal
   190  			return
   191  		}
   192  		dataList = [][]byte{data}
   193  		return
   194  	case reflect.Slice, reflect.Array:
   195  		if data, ok := rv.Interface().([]byte); ok {
   196  			dataList = [][]byte{data}
   197  			return
   198  		}
   199  		if in == "body" {
   200  			data, errForMarshal := GetContentTransformer(format).Marshal(rv.Interface())
   201  			if errForMarshal != nil {
   202  				err = errForMarshal
   203  				return
   204  			}
   205  			dataList = [][]byte{data}
   206  			return
   207  		}
   208  		for i := 0; i < rv.Len(); i++ {
   209  			itemsList, errForStringify := ContentMarshal(rv.Index(i), in, format)
   210  			if errForStringify != nil {
   211  				err = errForStringify
   212  				return
   213  			}
   214  			dataList = append(dataList, itemsList...)
   215  		}
   216  		return
   217  	default:
   218  		data, errForStringify := Stringify(rv.Interface())
   219  		for errForStringify != nil {
   220  			err = errForStringify
   221  		}
   222  		dataList = [][]byte{data}
   223  		return
   224  	}
   225  }
   226  
   227  func ContentUnmarshal(rv reflect.Value, tpe reflect.Type, in string, name string, format string, dataList ...[]byte) error {
   228  	if len(dataList) == 0 || len(dataList[0]) == 0 {
   229  		return nil
   230  	}
   231  
   232  	if rv.Kind() == reflect.Ptr && rv.IsNil() && rv.CanSet() {
   233  		rv.Set(reflect.New(reflectx.IndirectType(tpe)))
   234  	}
   235  
   236  	rv = reflect.Indirect(rv)
   237  
   238  	if textUnmarshaler, ok := rv.Addr().Interface().(encoding.TextUnmarshaler); ok {
   239  		if err := textUnmarshaler.UnmarshalText(dataList[0]); err != nil {
   240  			return err
   241  		}
   242  		return nil
   243  	}
   244  
   245  	switch rv.Kind() {
   246  	case reflect.Map, reflect.Struct:
   247  		if rv.CanAddr() {
   248  			rv = rv.Addr()
   249  		}
   250  		return structUnmarshal(in, name, format, dataList[0], rv.Interface())
   251  	case reflect.Slice:
   252  		if _, ok := rv.Interface().([]byte); ok {
   253  			rv.SetBytes(dataList[0])
   254  			return nil
   255  		}
   256  
   257  		if in == "body" {
   258  			if rv.CanAddr() {
   259  				rv = rv.Addr()
   260  			}
   261  			return structUnmarshal(in, name, format, dataList[0], rv.Interface())
   262  		}
   263  
   264  		sliceRv := reflect.MakeSlice(tpe, len(dataList), cap(dataList))
   265  
   266  		itemType := rv.Type().Elem()
   267  		for i, data := range dataList {
   268  			err := ContentUnmarshal(sliceRv.Index(i), itemType, in, name, format, data)
   269  			if err != nil {
   270  				return err
   271  			}
   272  		}
   273  
   274  		rv.Set(sliceRv)
   275  
   276  		return nil
   277  	case reflect.Array:
   278  		if in == "body" {
   279  			if rv.CanAddr() {
   280  				rv = rv.Addr()
   281  			}
   282  			return structUnmarshal(in, name, format, dataList[0], rv.Interface())
   283  		}
   284  		itemType := rv.Type().Elem()
   285  		for i, data := range dataList {
   286  			err := ContentUnmarshal(rv.Index(i), itemType, in, name, format, data)
   287  			if err != nil {
   288  				return err
   289  			}
   290  		}
   291  		return nil
   292  	default:
   293  		return strutil.ConvertFromStr(string(dataList[0]), rv)
   294  	}
   295  }
   296  
   297  func structUnmarshal(in string, rootField string, format string, data []byte, v interface{}) error {
   298  	err := GetContentTransformer(format).Unmarshal(data, v)
   299  	if err != nil {
   300  		statusError := status_error.InvalidBodyStruct.StatusError()
   301  		if unmarshalTypeErr, ok := err.(*json.UnmarshalTypeError); ok {
   302  			return statusError.WithErrorField(in, LocateJSONPath(data, unmarshalTypeErr.Offset), unmarshalTypeErr.Error())
   303  		}
   304  		return statusError.WithErrorField(in, rootField, "参数格式错误").WithDesc(err.Error())
   305  	}
   306  	return nil
   307  }