@@ -263,7 +263,7 @@ func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode
263263 return nil
264264}
265265
266- // This works much like BindStyledParameter, however it takes a query argument
266+ // BindQueryParameter works much like BindStyledParameter, however it takes a query argument
267267// input array from the url package, since query arguments come through a
268268// different path than the styled arguments. They're also exceptionally fussy.
269269// For example, consider the exploded and unexploded form parameter examples:
@@ -336,10 +336,12 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
336336 case reflect .Slice :
337337 // In the slice case, we simply use the arguments provided by
338338 // http library.
339+
339340 if ! found {
340341 if required {
341342 return fmt .Errorf ("query parameter '%s' is required" , paramName )
342343 } else {
344+ // If an optional parameter is not found, we do nothing,
343345 return nil
344346 }
345347 }
@@ -349,7 +351,13 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
349351 // form style object binding doesn't tell us which arguments
350352 // in the query string correspond to the object's fields. We'll
351353 // try to bind field by field.
352- err = bindParamsToExplodedObject (paramName , queryParams , output )
354+ var fieldsPresent bool
355+ fieldsPresent , err = bindParamsToExplodedObject (paramName , queryParams , output )
356+ // If no fields were set, and there is no error, we will not fall
357+ // through to assign the destination.
358+ if ! fieldsPresent {
359+ return nil
360+ }
353361 default :
354362 // Primitive object case. We expect to have 1 value to
355363 // unmarshal.
@@ -363,6 +371,15 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
363371 if len (values ) != 1 {
364372 return fmt .Errorf ("multiple values for single value parameter '%s'" , paramName )
365373 }
374+
375+ if ! found {
376+ if required {
377+ return fmt .Errorf ("query parameter '%s' is required" , paramName )
378+ } else {
379+ // If an optional parameter is not found, we do nothing,
380+ return nil
381+ }
382+ }
366383 err = BindStringToObject (values [0 ], output )
367384 }
368385 if err != nil {
@@ -427,21 +444,28 @@ func BindQueryParameter(style string, explode bool, required bool, paramName str
427444 }
428445}
429446
430- // This function reflects the destination structure, and pulls the value for
447+ // bindParamsToExplodedObject reflects the destination structure, and pulls the value for
431448// each settable field from the given parameters map. This is to deal with the
432449// exploded form styled object which may occupy any number of parameter names.
433450// We don't try to be smart here, if the field exists as a query argument,
434- // set its value.
435- func bindParamsToExplodedObject (paramName string , values url.Values , dest interface {}) error {
451+ // set its value. This function returns a boolean, telling us whether there was
452+ // anything to bind. There will be nothing to bind if a parameter isn't found by name,
453+ // or none of an exploded object's fields are present.
454+ func bindParamsToExplodedObject (paramName string , values url.Values , dest interface {}) (bool , error ) {
436455 // Dereference pointers to their destination values
437456 binder , v , t := indirect (dest )
438457 if binder != nil {
439- return BindStringToObject (values .Get (paramName ), dest )
458+ _ , found := values [paramName ]
459+ if ! found {
460+ return false , nil
461+ }
462+ return true , BindStringToObject (values .Get (paramName ), dest )
440463 }
441464 if t .Kind () != reflect .Struct {
442- return fmt .Errorf ("unmarshaling query arg '%s' into wrong type" , paramName )
465+ return false , fmt .Errorf ("unmarshaling query arg '%s' into wrong type" , paramName )
443466 }
444467
468+ fieldsPresent := false
445469 for i := 0 ; i < t .NumField (); i ++ {
446470 fieldT := t .Field (i )
447471
@@ -466,15 +490,16 @@ func bindParamsToExplodedObject(paramName string, values url.Values, dest interf
466490 fieldVal , found := values [fieldName ]
467491 if found {
468492 if len (fieldVal ) != 1 {
469- return fmt .Errorf ("field '%s' specified multiple times for param '%s'" , fieldName , paramName )
493+ return false , fmt .Errorf ("field '%s' specified multiple times for param '%s'" , fieldName , paramName )
470494 }
471495 err := BindStringToObject (fieldVal [0 ], v .Field (i ).Addr ().Interface ())
472496 if err != nil {
473- return fmt .Errorf ("could not bind query arg '%s' to request object: %s'" , paramName , err )
497+ return false , fmt .Errorf ("could not bind query arg '%s' to request object: %s'" , paramName , err )
474498 }
499+ fieldsPresent = true
475500 }
476501 }
477- return nil
502+ return fieldsPresent , nil
478503}
479504
480505// indirect
0 commit comments