pkg/rest/codec/codec.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)
}