github.com/cloudwego/hertz@v0.9.3/pkg/app/server/binding/internal/decoder/decoder.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 * MIT License 16 * 17 * Copyright (c) 2019-present Fenny and Contributors 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining a copy 20 * of this software and associated documentation files (the "Software"), to deal 21 * in the Software without restriction, including without limitation the rights 22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 * copies of the Software, and to permit persons to whom the Software is 24 * furnished to do so, subject to the following conditions: 25 * 26 * The above copyright notice and this permission notice shall be included in all 27 * copies or substantial portions of the Software. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 * SOFTWARE. 36 * 37 * This file may have been modified by CloudWeGo authors. All CloudWeGo 38 * Modifications are Copyright 2023 CloudWeGo Authors 39 */ 40 41 package decoder 42 43 import ( 44 "fmt" 45 "mime/multipart" 46 "reflect" 47 48 "github.com/cloudwego/hertz/pkg/protocol" 49 "github.com/cloudwego/hertz/pkg/route/param" 50 ) 51 52 type fieldDecoder interface { 53 Decode(req *protocol.Request, params param.Params, reqValue reflect.Value) error 54 } 55 56 type Decoder func(req *protocol.Request, params param.Params, rv reflect.Value) error 57 58 type DecodeConfig struct { 59 LooseZeroMode bool 60 DisableDefaultTag bool 61 DisableStructFieldResolve bool 62 EnableDecoderUseNumber bool 63 EnableDecoderDisallowUnknownFields bool 64 ValidateTag string 65 TypeUnmarshalFuncs map[reflect.Type]CustomizeDecodeFunc 66 } 67 68 func GetReqDecoder(rt reflect.Type, byTag string, config *DecodeConfig) (Decoder, bool, error) { 69 var decoders []fieldDecoder 70 var needValidate bool 71 72 el := rt.Elem() 73 if el.Kind() != reflect.Struct { 74 return nil, false, fmt.Errorf("unsupported \"%s\" type binding", rt.String()) 75 } 76 77 for i := 0; i < el.NumField(); i++ { 78 if el.Field(i).PkgPath != "" && !el.Field(i).Anonymous { 79 // ignore unexported field 80 continue 81 } 82 83 dec, needValidate2, err := getFieldDecoder(parentInfos{[]reflect.Type{el}, []int{}, ""}, el.Field(i), i, byTag, config) 84 if err != nil { 85 return nil, false, err 86 } 87 needValidate = needValidate || needValidate2 88 89 if dec != nil { 90 decoders = append(decoders, dec...) 91 } 92 } 93 94 return func(req *protocol.Request, params param.Params, rv reflect.Value) error { 95 for _, decoder := range decoders { 96 err := decoder.Decode(req, params, rv) 97 if err != nil { 98 return err 99 } 100 } 101 102 return nil 103 }, needValidate, nil 104 } 105 106 type parentInfos struct { 107 Types []reflect.Type 108 Indexes []int 109 JSONName string 110 } 111 112 func getFieldDecoder(pInfo parentInfos, field reflect.StructField, index int, byTag string, config *DecodeConfig) ([]fieldDecoder, bool, error) { 113 for field.Type.Kind() == reflect.Ptr { 114 field.Type = field.Type.Elem() 115 } 116 // skip anonymous definitions, like: 117 // type A struct { 118 // string 119 // } 120 if field.Type.Kind() != reflect.Struct && field.Anonymous { 121 return nil, false, nil 122 } 123 124 // JSONName is like 'a.b.c' for 'required validate' 125 fieldTagInfos, newParentJSONName, needValidate := lookupFieldTags(field, pInfo.JSONName, config) 126 if len(fieldTagInfos) == 0 && !config.DisableDefaultTag { 127 fieldTagInfos, newParentJSONName = getDefaultFieldTags(field, pInfo.JSONName) 128 } 129 if len(byTag) != 0 { 130 fieldTagInfos = getFieldTagInfoByTag(field, byTag) 131 } 132 133 // customized type decoder has the highest priority 134 if customizedFunc, exist := config.TypeUnmarshalFuncs[field.Type]; exist { 135 dec, err := getCustomizedFieldDecoder(field, index, fieldTagInfos, pInfo.Indexes, customizedFunc, config) 136 return dec, needValidate, err 137 } 138 139 // slice/array field decoder 140 if field.Type.Kind() == reflect.Slice || field.Type.Kind() == reflect.Array { 141 dec, err := getSliceFieldDecoder(field, index, fieldTagInfos, pInfo.Indexes, config) 142 return dec, needValidate, err 143 } 144 145 // map filed decoder 146 if field.Type.Kind() == reflect.Map { 147 dec, err := getMapTypeTextDecoder(field, index, fieldTagInfos, pInfo.Indexes, config) 148 return dec, needValidate, err 149 } 150 151 // struct field will be resolved recursively 152 if field.Type.Kind() == reflect.Struct { 153 var decoders []fieldDecoder 154 el := field.Type 155 // todo: more built-in common struct binding, ex. time... 156 switch el { 157 case reflect.TypeOf(multipart.FileHeader{}): // file binding 158 dec, err := getMultipartFileDecoder(field, index, fieldTagInfos, pInfo.Indexes, config) 159 return dec, needValidate, err 160 } 161 if !config.DisableStructFieldResolve { // decode struct type separately 162 structFieldDecoder, err := getStructTypeFieldDecoder(field, index, fieldTagInfos, pInfo.Indexes, config) 163 if err != nil { 164 return nil, needValidate, err 165 } 166 if structFieldDecoder != nil { 167 decoders = append(decoders, structFieldDecoder...) 168 } 169 } 170 171 // prevent infinite recursion when struct field with the same name as a struct 172 if hasSameType(pInfo.Types, el) { 173 return decoders, needValidate, nil 174 } 175 176 pIdx := pInfo.Indexes 177 for i := 0; i < el.NumField(); i++ { 178 if el.Field(i).PkgPath != "" && !el.Field(i).Anonymous { 179 // ignore unexported field 180 continue 181 } 182 var idxes []int 183 if len(pInfo.Indexes) > 0 { 184 idxes = append(idxes, pIdx...) 185 } 186 idxes = append(idxes, index) 187 pInfo.Indexes = idxes 188 pInfo.Types = append(pInfo.Types, el) 189 pInfo.JSONName = newParentJSONName 190 dec, needValidate2, err := getFieldDecoder(pInfo, el.Field(i), i, byTag, config) 191 needValidate = needValidate || needValidate2 192 if err != nil { 193 return nil, false, err 194 } 195 if dec != nil { 196 decoders = append(decoders, dec...) 197 } 198 } 199 200 return decoders, needValidate, nil 201 } 202 203 // base type decoder 204 dec, err := getBaseTypeTextDecoder(field, index, fieldTagInfos, pInfo.Indexes, config) 205 return dec, needValidate, err 206 } 207 208 // hasSameType determine if the same type is present in the parent-child relationship 209 func hasSameType(pts []reflect.Type, ft reflect.Type) bool { 210 for _, pt := range pts { 211 if reflect.DeepEqual(getElemType(pt), getElemType(ft)) { 212 return true 213 } 214 } 215 return false 216 }