@@ -13,6 +13,7 @@ const (
1313 defaultTagName = "param"
1414 queryTagValuePrefix = "query"
1515 pathTagValuePrefix = "path"
16+ formTagValuePrefix = "form"
1617)
1718
1819// TagResolver is a function that decides from a field tag what parameter should be searched.
@@ -34,6 +35,9 @@ func TagNameResolver(tagName string) TagResolver {
3435// PathParamFunc is a function that returns value of specified http path parameter.
3536type PathParamFunc func (r * http.Request , key string ) string
3637
38+ // FormParamFunc is a function that returns value of specified form parameter.
39+ type FormParamFunc func (r * http.Request , key string ) string
40+
3741// Parser can Parse query and path parameters from http.Request into a struct.
3842// Fields struct have to be tagged such that either QueryParamTagResolver or PathParamTagResolver returns
3943// valid parameter name from the provided tag.
@@ -43,6 +47,7 @@ type PathParamFunc func(r *http.Request, key string) string
4347type Parser struct {
4448 ParamTagResolver TagResolver
4549 PathParamFunc PathParamFunc
50+ FormParamFunc FormParamFunc
4651}
4752
4853// DefaultParser returns query and path parameter Parser with intended struct tags
@@ -51,6 +56,7 @@ func DefaultParser() Parser {
5156 return Parser {
5257 ParamTagResolver : TagNameResolver (defaultTagName ),
5358 PathParamFunc : nil , // keep nil, as there is no sensible default of how to get value of path parameter
59+ FormParamFunc : nil , // keep nil, as there is no sensible default of how to get value of form parameter
5460 }
5561}
5662
@@ -61,6 +67,13 @@ func (p Parser) WithPathParamFunc(f PathParamFunc) Parser {
6167 return p
6268}
6369
70+ // WithFormParamFunc returns a copy of Parser with set function for getting form parameters from http.Request.
71+ // For more see Parser description.
72+ func (p Parser ) WithFormParamFunc (f FormParamFunc ) Parser {
73+ p .FormParamFunc = f
74+ return p
75+ }
76+
6477// Parse accepts the request and a pointer to struct with its fields tagged with appropriate tags set in Parser.
6578// Such tagged fields must be in top level struct, or in exported struct embedded in top-level struct.
6679// All such tagged fields are assigned the respective parameter from the actual request.
@@ -113,6 +126,7 @@ type paramType int
113126const (
114127 paramTypeQuery paramType = iota
115128 paramTypePath
129+ paramTypeForm
116130)
117131
118132type taggedFieldIndexPath struct {
@@ -139,6 +153,7 @@ func (p Parser) findTaggedIndexPaths(typ reflect.Type, currentNestingIndexPath [
139153 }
140154 tag := typeField .Tag
141155 pathParamName , okPath := p .resolvePath (tag )
156+ formParamName , okForm := p .resolveForm (tag )
142157 queryParamName , okQuery := p .resolveQuery (tag )
143158 if okPath {
144159 newPath := make ([]int , 0 , len (currentNestingIndexPath )+ 1 )
@@ -150,6 +165,16 @@ func (p Parser) findTaggedIndexPaths(typ reflect.Type, currentNestingIndexPath [
150165 indexPath : newPath ,
151166 })
152167 }
168+ if okForm {
169+ newPath := make ([]int , 0 , len (currentNestingIndexPath )+ 1 )
170+ newPath = append (newPath , currentNestingIndexPath ... )
171+ newPath = append (newPath , i )
172+ paths = append (paths , taggedFieldIndexPath {
173+ paramType : paramTypeForm ,
174+ paramName : formParamName ,
175+ indexPath : newPath ,
176+ })
177+ }
153178 if okQuery {
154179 newPath := make ([]int , 0 , len (currentNestingIndexPath )+ 1 )
155180 newPath = append (newPath , currentNestingIndexPath ... )
@@ -194,6 +219,11 @@ func (p Parser) parseParam(r *http.Request, path taggedFieldIndexPath) error {
194219 if err != nil {
195220 return err
196221 }
222+ case paramTypeForm :
223+ err := p .parseFormParam (r , path .paramName , path .destValue )
224+ if err != nil {
225+ return err
226+ }
197227 case paramTypeQuery :
198228 err := p .parseQueryParam (r , path .paramName , path .destValue )
199229 if err != nil {
@@ -217,6 +247,22 @@ func (p Parser) parsePathParam(r *http.Request, paramName string, v reflect.Valu
217247 return nil
218248}
219249
250+ func (p Parser ) parseFormParam (r * http.Request , paramName string , v reflect.Value ) error {
251+ if r .Method != http .MethodPost && r .Method != http .MethodPut && r .Method != http .MethodPatch {
252+ return fmt .Errorf ("struct's field was tagged for parsing the form parameter (%s) but request method is not POST, PUT or PATCH" , paramName )
253+ }
254+ if err := r .ParseForm (); err != nil {
255+ return fmt .Errorf ("parsing form data: %w" , err )
256+ }
257+ if values , ok := r .Form [paramName ]; ok && len (values ) > 0 {
258+ err := unmarshalValueOrSlice (values , v )
259+ if err != nil {
260+ return fmt .Errorf ("unmarshaling form parameter %s: %w" , paramName , err )
261+ }
262+ }
263+ return nil
264+ }
265+
220266func (p Parser ) parseQueryParam (r * http.Request , paramName string , v reflect.Value ) error {
221267 query := r .URL .Query ()
222268 if values , ok := query [paramName ]; ok && len (values ) > 0 {
@@ -331,6 +377,10 @@ func (p Parser) resolvePath(fieldTag reflect.StructTag) (string, bool) {
331377 return p .resolveTagWithModifier (fieldTag , pathTagValuePrefix )
332378}
333379
380+ func (p Parser ) resolveForm (fieldTag reflect.StructTag ) (string , bool ) {
381+ return p .resolveTagWithModifier (fieldTag , formTagValuePrefix )
382+ }
383+
334384func (p Parser ) resolveQuery (fieldTag reflect.StructTag ) (string , bool ) {
335385 return p .resolveTagWithModifier (fieldTag , queryTagValuePrefix )
336386}
0 commit comments