134 lines
3.2 KiB
Go
134 lines
3.2 KiB
Go
/*
|
|
* Tencent is pleased to support the open source community by making
|
|
* 蓝鲸智云 - 配置平台 (BlueKing - CMDB) available.
|
|
* Copyright (C) 2025 Tencent. All rights reserved.
|
|
* Licensed under the MIT License (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at http://opensource.org/licenses/MIT
|
|
* 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.
|
|
* We undertake not to change the open source license (MIT license) applicable
|
|
* to the current version of the project delivered to anyone in the future.
|
|
*/
|
|
|
|
// Package codec provides encoding and decoding utilities across various formats
|
|
package codec
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"reflect"
|
|
)
|
|
|
|
func decodeTo(r *http.Request, val any) error {
|
|
rt := reflect.TypeOf(val).Elem()
|
|
rv := reflect.ValueOf(val).Elem()
|
|
|
|
// json 整个解析
|
|
jsonCodec := NewJsonCodec(r)
|
|
if err := jsonCodec.Decode(val); err != nil {
|
|
return err
|
|
}
|
|
|
|
formCodec, err := NewFormCodec(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pathCodec := NewPathCodec(r)
|
|
queryCodec := NewQueryCodec(r)
|
|
headerCodec := NewHeaderCodec(r)
|
|
|
|
for i := 0; i < rt.NumField(); i++ {
|
|
field := rt.Field(i)
|
|
|
|
// 非导出需要跳过, 无法设置值
|
|
if !field.IsExported() {
|
|
continue
|
|
}
|
|
|
|
tagStr := field.Tag.Get(tagName)
|
|
if tagStr == "" {
|
|
continue
|
|
}
|
|
tag, err := parseTag(tagStr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fv := rv.Field(i)
|
|
if err := formCodec.Decode(field, fv, tag); err != nil {
|
|
return err
|
|
}
|
|
if err := queryCodec.Decode(field, fv, tag); err != nil {
|
|
return err
|
|
}
|
|
if err := headerCodec.Decode(field, fv, tag); err != nil {
|
|
return err
|
|
}
|
|
if err := pathCodec.Decode(field, fv, tag); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Decode 按结构体反序列化Request
|
|
func Decode[T any](r *http.Request) (*T, error) {
|
|
rt := reflect.TypeFor[T]()
|
|
if rt.Kind() != reflect.Struct {
|
|
return nil, errors.New("generic type T must be a struct type")
|
|
}
|
|
|
|
t := new(T)
|
|
err := decodeTo(r, t)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("codec decode: %w", err)
|
|
}
|
|
|
|
return t, nil
|
|
}
|
|
|
|
// getFieldValue 获取字段值
|
|
func getFieldValue(field reflect.Type, tag *Tag, values []string) (reflect.Value, error) {
|
|
// 指针类型
|
|
if field.Kind() == reflect.Pointer {
|
|
typ := field.Elem()
|
|
rv, err := getFieldValue(typ, tag, values)
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
|
|
newPtr := reflect.New(typ)
|
|
newPtr.Elem().Set(rv)
|
|
return newPtr, nil
|
|
}
|
|
|
|
// slice类型
|
|
if field.Kind() == reflect.Slice {
|
|
typ := field.Elem()
|
|
|
|
// []byte 特殊处理
|
|
if typ == byteType {
|
|
return ParseValue(field, values[0], tag.Option)
|
|
}
|
|
|
|
val := reflect.MakeSlice(field, 0, len(values))
|
|
for _, v := range values {
|
|
rv, err := getFieldValue(typ, tag, []string{v})
|
|
if err != nil {
|
|
return reflect.Value{}, err
|
|
}
|
|
val = reflect.Append(val, rv)
|
|
}
|
|
return val, nil
|
|
}
|
|
|
|
return ParseValue(field, values[0], tag.Option)
|
|
}
|