github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/transformer/tsfm_urlencoded.go (about)

     1  package transformer
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net/textproto"
     7  	"net/url"
     8  	"reflect"
     9  
    10  	pkgerr "github.com/pkg/errors"
    11  
    12  	"github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/httpx"
    13  	vldterr "github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors"
    14  	"github.com/machinefi/w3bstream/pkg/depends/x/reflectx"
    15  	"github.com/machinefi/w3bstream/pkg/depends/x/typesx"
    16  )
    17  
    18  func init() { DefaultFactory.Register(&URLEncoded{}) }
    19  
    20  /*
    21  URLEncoded for application/x-www-form-urlencoded
    22  
    23  	var s = struct {
    24  		Username string `name:"username"`
    25  		Nickname string `name:"username,omitempty"`
    26  		Tags []string   `name:"tag"`
    27  	}{
    28  		Username: "name",
    29  		Tags: []string{"1","2"},
    30  	}
    31  
    32  will transform to
    33  
    34  	username=name&tag=1&tag=2
    35  */
    36  type URLEncoded struct{ *FlattenParams }
    37  
    38  func (URLEncoded) Names() []string {
    39  	return []string{httpx.MIME_FORM_URLENCODED, "form", "urlencoded", "url-encoded"}
    40  }
    41  
    42  func (t URLEncoded) String() string { return httpx.MIME_FORM_URLENCODED }
    43  
    44  func (URLEncoded) NamedByTag() string { return "name" }
    45  
    46  func (URLEncoded) New(ctx context.Context, typ typesx.Type) (Transformer, error) {
    47  	tsf := &URLEncoded{}
    48  
    49  	typ = typesx.DeRef(typ)
    50  	if typ.Kind() != reflect.Struct {
    51  		return nil, pkgerr.Errorf(
    52  			"content transformer `%s` should be used for struct type",
    53  			tsf,
    54  		)
    55  	}
    56  
    57  	tsf.FlattenParams = &FlattenParams{}
    58  
    59  	if err := tsf.FlattenParams.CollectParams(ctx, typ); err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	return tsf, nil
    64  }
    65  
    66  func (t *URLEncoded) EncodeTo(ctx context.Context, w io.Writer, v interface{}) error {
    67  	rv, ok := v.(reflect.Value)
    68  	if !ok {
    69  		rv = reflect.ValueOf(v)
    70  	}
    71  	rv = reflectx.Indirect(rv)
    72  
    73  	values := url.Values{}
    74  	errs := vldterr.NewErrorSet()
    75  
    76  	for i := range t.Params {
    77  		p := t.Params[i]
    78  
    79  		if p.Tsf != nil {
    80  			field := p.FieldValue(rv)
    81  			builders := NewStringBuilders()
    82  			if err := NewSuper(p.Tsf, &p.Option.CommonOption).
    83  				EncodeTo(ctx, builders, field); err != nil {
    84  				errs.AddErr(err, p.Name)
    85  				continue
    86  			}
    87  			values[p.Name] = builders.StringSlice()
    88  		}
    89  	}
    90  
    91  	if err := errs.Err(); err != nil {
    92  		return err
    93  	}
    94  
    95  	httpx.MaybeWriteHeader(
    96  		ctx, w, t.Names()[0],
    97  		map[string]string{
    98  			"param": "value",
    99  		},
   100  	)
   101  	_, err := w.Write([]byte(values.Encode()))
   102  	return err
   103  }
   104  
   105  func (t *URLEncoded) DecodeFrom(ctx context.Context, r io.Reader, v interface{}, headers ...textproto.MIMEHeader) error {
   106  	rv, ok := v.(reflect.Value)
   107  	if !ok {
   108  		rv = reflect.ValueOf(v)
   109  	}
   110  
   111  	if rv.Kind() != reflect.Ptr {
   112  		return pkgerr.New("decode target must be ptr value")
   113  	}
   114  
   115  	data, err := io.ReadAll(r)
   116  	if err != nil {
   117  		return err
   118  	}
   119  
   120  	values, err := url.ParseQuery(string(data))
   121  	if err != nil {
   122  		return err
   123  	}
   124  
   125  	errs := vldterr.NewErrorSet()
   126  	for i := range t.Params {
   127  		p := t.Params[i]
   128  		fvs := values[p.Name]
   129  
   130  		if p.Tsf == nil || len(fvs) == 0 {
   131  			continue
   132  		}
   133  		if err := NewSuper(p.Tsf, &p.Option.CommonOption).
   134  			DecodeFrom(ctx, NewStringReaders(fvs), p.FieldValue(rv).Addr()); err != nil {
   135  			errs.AddErr(err, p.Name)
   136  			continue
   137  		}
   138  	}
   139  	return errs.Err()
   140  }