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 }