trpc.group/trpc-go/trpc-go@v1.0.2/http/serialization_form.go (about) 1 // 2 // 3 // Tencent is pleased to support the open source community by making tRPC available. 4 // 5 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 6 // All rights reserved. 7 // 8 // If you have downloaded a copy of the tRPC source code from Tencent, 9 // please note that tRPC source code is licensed under the Apache 2.0 License, 10 // A copy of the Apache 2.0 License is included in this file. 11 // 12 // 13 14 package http 15 16 import ( 17 "fmt" 18 "net/url" 19 20 "trpc.group/trpc-go/trpc-go/codec" 21 22 "github.com/go-playground/form/v4" 23 "github.com/mitchellh/mapstructure" 24 ) 25 26 // Uses the same tag as json. 27 var tag = "json" 28 29 func init() { 30 codec.RegisterSerializer( 31 codec.SerializationTypeForm, 32 NewFormSerialization(tag), 33 ) 34 } 35 36 // NewFormSerialization initializes the form serialized object. 37 func NewFormSerialization(tag string) codec.Serializer { 38 encoder := form.NewEncoder() 39 encoder.SetTagName(tag) 40 decoder := form.NewDecoder() 41 decoder.SetTagName(tag) 42 return &FormSerialization{ 43 tagname: tag, 44 encoder: encoder, 45 decoder: decoder, 46 } 47 } 48 49 // FormSerialization packages the kv structure of http get request. 50 type FormSerialization struct { 51 tagname string 52 encoder *form.Encoder 53 decoder *form.Decoder 54 } 55 56 // Unmarshal unpacks kv structure. 57 func (j *FormSerialization) Unmarshal(in []byte, body interface{}) error { 58 values, err := url.ParseQuery(string(in)) 59 if err != nil { 60 return err 61 } 62 switch body.(type) { 63 // go-playground/form does not support map structure. 64 case map[string]interface{}, *map[string]interface{}, map[string]string, *map[string]string: 65 return unmarshalValues(j.tagname, values, body) 66 default: 67 } 68 // First try using go-playground/form, it can handle nested struct. 69 // But it cannot handle Chinese characters in byte slice. 70 err = j.decoder.Decode(body, values) 71 if err == nil { 72 return nil 73 } 74 // Second try using mapstructure. 75 if e := unmarshalValues(j.tagname, values, body); e != nil { 76 return fmt.Errorf("unmarshal error: first try err = %+v, second try err = %w", err, e) 77 } 78 return nil 79 } 80 81 // unmarshalValues parses the corresponding fields in values according to tagname. 82 func unmarshalValues(tagname string, values url.Values, body interface{}) error { 83 params := map[string]interface{}{} 84 for k, v := range values { 85 if len(v) == 1 { 86 params[k] = v[0] 87 } else { 88 params[k] = v 89 } 90 } 91 config := &mapstructure.DecoderConfig{TagName: tagname, Result: body, WeaklyTypedInput: true, Metadata: nil} 92 decoder, err := mapstructure.NewDecoder(config) 93 if err != nil { 94 return err 95 } 96 return decoder.Decode(params) 97 } 98 99 // Marshal packages kv structure. 100 func (j *FormSerialization) Marshal(body interface{}) ([]byte, error) { 101 if req, ok := body.(url.Values); ok { // Used to send form urlencode post request to backend. 102 return []byte(req.Encode()), nil 103 } 104 val, err := j.encoder.Encode(body) 105 if err != nil { 106 return nil, err 107 } 108 return []byte(val.Encode()), nil 109 }