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  }