/
parampack.go
405 lines (337 loc) · 12.1 KB
/
parampack.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
// Copyright 2013 slowfei And The Contributors All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Create on 2013-8-16
// Update on 2014-07-11
// Email slowfei#foxmail.com
// Home http://www.slowfei.com
//
// web form params pack struct
//
package leafveingo
import (
"github.com/slowfei/gosfcore/log"
"github.com/slowfei/gosfcore/utils/reflect"
"mime"
"mime/multipart"
"net/http"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
)
var (
// 判断数组标识的正则
_arrayTagRex = regexp.MustCompile("\\[\\d*\\]")
)
//
// form params pack struct
//
type parampack struct {
params url.Values // form or get params
files map[string][]*multipart.FileHeader // file
fileNum int // all file number
}
/**
* parse form
*
* @param req
* @return param form params pack struct
* @return err
*/
func parampackParseForm(req *http.Request, fileUploadSize int64) (param *parampack, err error) {
switch req.Method {
case "GET":
param = new(parampack)
param.params = req.URL.Query()
case "POST":
contentType := req.Header.Get("Content-Type")
enctype, _, e := mime.ParseMediaType(contentType)
if nil != e {
err = e
return
}
param = new(parampack)
switch {
case enctype == "application/x-www-form-urlencoded":
err = req.ParseForm()
if nil != err {
return
}
// 考虑安全因素,让调用则知道请求的参数来自Form还是Query所以进行POST请求就只获取Form的参数
param.params = req.PostForm
case enctype == "multipart/form-data":
err = req.ParseMultipartForm(fileUploadSize)
if nil != err {
return
}
// ParseMultipartForm()解析已经调用了ParseForm()
param.params = url.Values(req.MultipartForm.Value)
if nil != req.MultipartForm && 0 < len(req.MultipartForm.File) {
for k, v := range req.MultipartForm.File {
// 添加空字符串的主要目的是为了能够在创建结构时初始化切片的数量
param.params.Set(k, "")
fNum := len(v)
if 1 < fNum {
param.fileNum += fNum
} else {
param.fileNum++
}
}
param.files = req.MultipartForm.File
}
}
default:
param = new(parampack)
param.params = req.URL.Query()
}
return
}
/**
* 根据field的字段名称设置struct字段属性值
* 字段名称可为"type.tag.TagName", 以"."作为子字段的名称分割
*
* @param sutValue 被设值的结构反射类型
* @param fieldName 字段名(type.tag.TagName) or (tagnName)
* @param value 设值值
*/
func parampackSetStructFieldValue(sutValue reflect.Value, fieldName string, value interface{}) {
fieldName = strings.TrimSpace(fieldName)
sutValueElem := reflect.Indirect(sutValue)
if len(fieldName) == 0 {
return
}
// 由于考虑到key的值可能为(type.tag.TagName),嵌套的赋值,所以需要进行"."的分割,每次获取slice的第一项
fieldsName := strings.Split(fieldName, ".")
childFieldName := strings.Title(fieldsName[0])
if sutValueElem.Kind() == reflect.Slice {
// 集合字段的设值操作
strIndex := _arrayTagRex.FindString(childFieldName)
if 0 >= len(strIndex) {
// 判断如果是数组类型,但是设值的字段名称不是数组的标识则跳过(数组标识Users[0])
// 由于有可能是需要直接设置数组参数,但是字段名称中(childFieldName = "Files")中未包含"[\d]"的标识
// 所以尝试直接设值
parampackSetParamValue(sutValueElem, fieldName, value)
return
}
intIndex, e := strconv.Atoi(strIndex[1 : len(strIndex)-1])
if nil == e && intIndex < sutValueElem.Len() {
fieldValue := sutValueElem.Index(intIndex)
parampackSetFieldValue(fieldValue, fieldName, fieldsName, value)
}
} else {
// 判断是否为数组标识,如果是的话就删除,例如:Users[0] 删除[0] = Users
// 这样便于FieldByName查找到相应的字段信息
reIndex := _arrayTagRex.FindStringIndex(childFieldName)
if 0 < len(reIndex) {
childFieldName = childFieldName[:reIndex[0]]
}
// 查找字段
fieldValue := sutValueElem.FieldByName(childFieldName)
parampackSetFieldValue(fieldValue, fieldName, fieldsName, value)
}
}
/**
* 根据url和form参数信息创建一个struct体,如果存在集合结构会根据form参数信息make分配切片大小。
*
* @param structType 需要操作的的结构
* @param urlValues url或form请求参数(主要为了操作slice的创建元素)
* @return 返回创建好的函数反射对象信息(指针类型的)
*/
func parampackNewStructPtr(structType reflect.Type, urlValues url.Values) reflect.Value {
// 考虑到结构内包含指针,如果不进行new的话直接(.)会爆出空指针异常,所以这里需要遍历每个对象
if structType.Kind() != reflect.Struct {
return reflect.Zero(structType)
}
structValue := reflect.New(structType)
if 0 == len(urlValues) {
return structValue
}
// begin 分析数组 TODO 需要调整集合下的集合 type[0].tmpe[0]
arraySizeMap := make(map[string]int) // key = jsonfiledname ,value = array size
for k, _ := range urlValues {
// 存储(oneUser.hobbys.names)操作的连接名 如果存在集合则存储为key
tempJoinName := ""
fields := strings.Split(k, ".")
for _, field := range fields {
fieldLower := strings.ToLower(field)
strIndex := _arrayTagRex.FindString(fieldLower)
if 0 != len(strIndex) {
intIndex, err := strconv.Atoi(strIndex[1 : len(strIndex)-1])
if nil == err {
// 这里为key做准备
tempJoinName += fieldLower[:len(fieldLower)-len(strIndex)]
// 由于下标是从0开始计算,所以需要+1作为 array size
intIndex += 1
if sizeIndex, ok := arraySizeMap[tempJoinName]; ok {
if sizeIndex < intIndex {
arraySizeMap[tempJoinName] = intIndex
}
} else {
arraySizeMap[tempJoinName] = intIndex
}
// 由于可能存在数组下还存在集合(temp[0].temp2[0]),所以继续累加"[index]."继续执行。
tempJoinName += strIndex + "."
} else {
// 进入到这部基本上是获取的 "[ 错误下标 ]" 才导致的,所欲当没有下标出现,继续累加
tempJoinName += fieldLower + "."
}
} else {
tempJoinName += fieldLower + "."
}
}
}
// end 分析数组
// 执行递归子字段操作
parampackNewStructFindFiled(structValue, "", arraySizeMap)
return structValue
}
/**
* 创建结构体递归遍历子字段操作
*
* @param structV 递归操作字段
* @param joinFieldName 字段连接名
* @param arraySizeMap 解析后的集合数量设值map
*/
func parampackNewStructFindFiled(structV reflect.Value, joinFieldName string, arraySizeMap map[string]int) {
structV = reflect.Indirect(structV)
if reflect.Struct != structV.Kind() {
return
}
// 遍历结构字段寻找需要初始化的slice
filedCount := structV.NumField()
for i := 0; i < filedCount; i++ {
childField := structV.Field(i)
childFieldType := childField.Type()
childFieldName := structV.Type().Field(i).Name
if !childField.CanSet() {
continue
}
if 0 != len(joinFieldName) && '.' != joinFieldName[len(joinFieldName)-1] {
joinFieldName += "."
}
if reflect.Ptr == childFieldType.Kind() && childField.IsNil() {
// 初始化子结构指针的操作
childField.Set(reflect.New(childField.Type().Elem()))
}
switch reflect.Indirect(childField).Kind() {
case reflect.Struct:
if !parampackFilterParamPackStructType(childField.Type()) {
parampackNewStructFindFiled(childField, joinFieldName+childFieldName, arraySizeMap)
}
case reflect.Slice:
tempJoinName := joinFieldName + childFieldName
if sliceSize, ok := arraySizeMap[strings.ToLower(tempJoinName)]; ok {
valueElem := reflect.Indirect(childField)
valueElem.Set(reflect.MakeSlice(valueElem.Type(), sliceSize, sliceSize))
// 继续执行集合子元素的遍历
if !parampackFilterParamPackStructType(valueElem.Type()) {
for j := 0; j < sliceSize; j++ {
childIndex := valueElem.Index(j)
if reflect.Ptr == childIndex.Kind() && childIndex.IsNil() {
// 初始化子元素指针的操作
childIndex.Set(reflect.New(childIndex.Type().Elem()))
}
// 当前由于是数组类型,所以传递连接名的时候加上"[index]"下标往下进行操作
tempJoinName = joinFieldName + childFieldName + "[" + strconv.Itoa(j) + "]"
parampackNewStructFindFiled(childIndex, tempJoinName, arraySizeMap)
}
}
}
}
}
}
/**
* set param value
*
* @param fieldValue 字段的反射对象
* @param fieldName 字段名
* @param value set value
*/
func parampackSetParamValue(fieldValue reflect.Value, fieldName string, value interface{}) {
err := SFReflectUtil.SetBaseTypeValue(fieldValue, value)
if nil != err {
SFLog.Error("%s set value error: %s", fieldName, err.Error())
}
}
/**
* 过滤参数封装一些系统的struct
* 在有些设置结构参数时,系统的struct不必要再递归分析
*
* @return true 属于过滤字段 false不是过滤的字段
*/
func parampackFilterParamPackStructType(valueType reflect.Type) bool {
result := true
// strings.Index(fieldValue.Type().String(), "multipart.FileHeader")
switch valueType.String() {
case "[]multipart.FileHeader", "[]*multipart.FileHeader", "*multipart.FileHeader", "multipart.FileHeader":
case "":
SFLog.Error("can not read type.String() : %v", valueType)
default:
result = false
}
return result
}
/**
* 针对字段进行设值
*
* @param fieldValue 字段反射对象
* @param fieldName 字段名,以便递归寻找下个设值字段
* @param fieldSplitName 字段名分割集合,以"."进行分割,主要是为了递归子字段进行拼接传递
* @param value 设置值
*/
func parampackSetFieldValue(fieldValue reflect.Value, fieldName string, fieldSplitName []string, value interface{}) {
if fieldValue.IsValid() {
// 为递归下一个参数做准备,保留后面的参数名(tag.TagName)
isRec := false
joinLaterFieldName := ""
if 1 < len(fieldSplitName) {
joinLaterFieldName = strings.Join(fieldSplitName[1:], ".")
isRec = true
// 进入这里表明还需要进行一次字段查询,所以需要进行递归操作,直到截取到最后一位的参数名标识(TagName)
}
switch fieldValue.Kind() {
case reflect.Ptr:
if fieldValue.IsNil() {
fieldValue.Set(reflect.New(fieldValue.Type().Elem()))
}
// 指针与非指针区分开,主要是在进行参数设值的时候对应与设置值相同的类型,减免指针的过多操作。
switch fieldValue.Elem().Kind() {
case reflect.Struct:
if isRec && !parampackFilterParamPackStructType(fieldValue.Type().Elem()) {
parampackSetStructFieldValue(fieldValue, joinLaterFieldName, value)
} else {
parampackSetParamValue(fieldValue, fieldName, value)
}
case reflect.Slice:
// 如果属于切片类型,传递fieldName主要是在操作一遍集合元素的赋值,因为不是结构类型无需再向下查找
parampackSetStructFieldValue(fieldValue, fieldName, value)
default:
parampackSetParamValue(fieldValue, fieldName, value)
}
case reflect.Struct:
if isRec && !parampackFilterParamPackStructType(fieldValue.Type()) {
parampackSetStructFieldValue(fieldValue, joinLaterFieldName, value)
} else {
// 如果检测的是系统或则非用户定义的struct就可以直接赋值了,赋值那里已经做了匹配类型才进行赋值的处理
parampackSetParamValue(fieldValue, fieldName, value)
}
case reflect.Slice:
parampackSetStructFieldValue(fieldValue, fieldName, value)
default:
parampackSetParamValue(fieldValue, fieldName, value)
}
}
}