github.com/cloudwego/hertz@v0.9.3/pkg/app/server/binding/config.go (about)

     1  /*
     2   * Copyright 2023 CloudWeGo Authors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package binding
    18  
    19  import (
    20  	stdJson "encoding/json"
    21  	"fmt"
    22  	"reflect"
    23  	"time"
    24  
    25  	exprValidator "github.com/bytedance/go-tagexpr/v2/validator"
    26  	inDecoder "github.com/cloudwego/hertz/pkg/app/server/binding/internal/decoder"
    27  	hJson "github.com/cloudwego/hertz/pkg/common/json"
    28  	"github.com/cloudwego/hertz/pkg/protocol"
    29  	"github.com/cloudwego/hertz/pkg/route/param"
    30  )
    31  
    32  // BindConfig contains options for default bind behavior.
    33  type BindConfig struct {
    34  	// LooseZeroMode if set to true,
    35  	// the empty string request parameter is bound to the zero value of parameter.
    36  	// NOTE:
    37  	//	The default is false.
    38  	//	Suitable for these parameter types: query/header/cookie/form .
    39  	LooseZeroMode bool
    40  	// DisableDefaultTag is used to add default tags to a field when it has no tag
    41  	// If is false, the field with no tag will be added default tags, for more automated binding. But there may be additional overhead.
    42  	// NOTE:
    43  	// The default is false.
    44  	DisableDefaultTag bool
    45  	// DisableStructFieldResolve is used to generate a separate decoder for a struct.
    46  	// If is false, the 'struct' field will get a single inDecoder.structTypeFieldTextDecoder, and use json.Unmarshal for decode it.
    47  	// It usually used to add json string to query parameter.
    48  	// NOTE:
    49  	// The default is false.
    50  	DisableStructFieldResolve bool
    51  	// EnableDecoderUseNumber is used to call the UseNumber method on the JSON
    52  	// Decoder instance. UseNumber causes the Decoder to unmarshal a number into an
    53  	// interface{} as a Number instead of as a float64.
    54  	// NOTE:
    55  	// The default is false.
    56  	// It is used for BindJSON().
    57  	EnableDecoderUseNumber bool
    58  	// EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method
    59  	// on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to
    60  	// return an error when the destination is a struct and the input contains object
    61  	// keys which do not match any non-ignored, exported fields in the destination.
    62  	// NOTE:
    63  	// The default is false.
    64  	// It is used for BindJSON().
    65  	EnableDecoderDisallowUnknownFields bool
    66  	// TypeUnmarshalFuncs registers customized type unmarshaler.
    67  	// NOTE:
    68  	// time.Time is registered by default
    69  	TypeUnmarshalFuncs map[reflect.Type]inDecoder.CustomizeDecodeFunc
    70  	// Validator is used to validate for BindAndValidate()
    71  	Validator StructValidator
    72  }
    73  
    74  func NewBindConfig() *BindConfig {
    75  	return &BindConfig{
    76  		LooseZeroMode:                      false,
    77  		DisableDefaultTag:                  false,
    78  		DisableStructFieldResolve:          false,
    79  		EnableDecoderUseNumber:             false,
    80  		EnableDecoderDisallowUnknownFields: false,
    81  		TypeUnmarshalFuncs:                 make(map[reflect.Type]inDecoder.CustomizeDecodeFunc),
    82  		Validator:                          defaultValidate,
    83  	}
    84  }
    85  
    86  // RegTypeUnmarshal registers customized type unmarshaler.
    87  func (config *BindConfig) RegTypeUnmarshal(t reflect.Type, fn inDecoder.CustomizeDecodeFunc) error {
    88  	// check
    89  	switch t.Kind() {
    90  	case reflect.String, reflect.Bool,
    91  		reflect.Float32, reflect.Float64,
    92  		reflect.Int, reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8,
    93  		reflect.Uint, reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8:
    94  		return fmt.Errorf("registration type cannot be a basic type")
    95  	case reflect.Ptr:
    96  		return fmt.Errorf("registration type cannot be a pointer type")
    97  	}
    98  	if config.TypeUnmarshalFuncs == nil {
    99  		config.TypeUnmarshalFuncs = make(map[reflect.Type]inDecoder.CustomizeDecodeFunc)
   100  	}
   101  	config.TypeUnmarshalFuncs[t] = fn
   102  	return nil
   103  }
   104  
   105  // MustRegTypeUnmarshal registers customized type unmarshaler. It will panic if exist error.
   106  func (config *BindConfig) MustRegTypeUnmarshal(t reflect.Type, fn func(req *protocol.Request, params param.Params, text string) (reflect.Value, error)) {
   107  	err := config.RegTypeUnmarshal(t, fn)
   108  	if err != nil {
   109  		panic(err)
   110  	}
   111  }
   112  
   113  func (config *BindConfig) initTypeUnmarshal() {
   114  	config.MustRegTypeUnmarshal(reflect.TypeOf(time.Time{}), func(req *protocol.Request, params param.Params, text string) (reflect.Value, error) {
   115  		if text == "" {
   116  			return reflect.ValueOf(time.Time{}), nil
   117  		}
   118  		t, err := time.Parse(time.RFC3339, text)
   119  		if err != nil {
   120  			return reflect.Value{}, err
   121  		}
   122  		return reflect.ValueOf(t), nil
   123  	})
   124  }
   125  
   126  // UseThirdPartyJSONUnmarshaler uses third-party json library for binding
   127  // NOTE:
   128  //
   129  //	UseThirdPartyJSONUnmarshaler will remain in effect once it has been called.
   130  func (config *BindConfig) UseThirdPartyJSONUnmarshaler(fn func(data []byte, v interface{}) error) {
   131  	hJson.Unmarshal = fn
   132  }
   133  
   134  // UseStdJSONUnmarshaler uses encoding/json as json library
   135  // NOTE:
   136  //
   137  //	The current version uses encoding/json by default.
   138  //	UseStdJSONUnmarshaler will remain in effect once it has been called.
   139  func (config *BindConfig) UseStdJSONUnmarshaler() {
   140  	config.UseThirdPartyJSONUnmarshaler(stdJson.Unmarshal)
   141  }
   142  
   143  type ValidateErrFactory func(fieldSelector, msg string) error
   144  
   145  type ValidateConfig struct {
   146  	ValidateTag string
   147  	ErrFactory  ValidateErrFactory
   148  }
   149  
   150  func NewValidateConfig() *ValidateConfig {
   151  	return &ValidateConfig{}
   152  }
   153  
   154  // MustRegValidateFunc registers validator function expression.
   155  // NOTE:
   156  //
   157  //	If force=true, allow to cover the existed same funcName.
   158  //	MustRegValidateFunc will remain in effect once it has been called.
   159  func (config *ValidateConfig) MustRegValidateFunc(funcName string, fn func(args ...interface{}) error, force ...bool) {
   160  	exprValidator.MustRegFunc(funcName, fn, force...)
   161  }
   162  
   163  // SetValidatorErrorFactory customizes the factory of validation error.
   164  func (config *ValidateConfig) SetValidatorErrorFactory(errFactory ValidateErrFactory) {
   165  	config.ErrFactory = errFactory
   166  }
   167  
   168  // SetValidatorTag customizes the factory of validation error.
   169  func (config *ValidateConfig) SetValidatorTag(tag string) {
   170  	config.ValidateTag = tag
   171  }