bindparam.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. // Copyright 2019 DeepMap, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package runtime
  15. import (
  16. "encoding"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "net/url"
  21. "reflect"
  22. "strings"
  23. "time"
  24. "github.com/deepmap/oapi-codegen/pkg/types"
  25. )
  26. // BindStyledParameter binds a parameter as described in the Path Parameters
  27. // section here to a Go object:
  28. // https://swagger.io/docs/specification/serialization/
  29. // It is a backward compatible function to clients generated with codegen
  30. // up to version v1.5.5. v1.5.6+ calls the function below.
  31. func BindStyledParameter(style string, explode bool, paramName string,
  32. value string, dest interface{}) error {
  33. return BindStyledParameterWithLocation(style, explode, paramName, ParamLocationUndefined, value, dest)
  34. }
  35. // BindStyledParameterWithLocation binds a parameter as described in the Path Parameters
  36. // section here to a Go object:
  37. // https://swagger.io/docs/specification/serialization/
  38. func BindStyledParameterWithLocation(style string, explode bool, paramName string,
  39. paramLocation ParamLocation, value string, dest interface{}) error {
  40. if value == "" {
  41. return fmt.Errorf("parameter '%s' is empty, can't bind its value", paramName)
  42. }
  43. // Based on the location of the parameter, we need to unescape it properly.
  44. var err error
  45. switch paramLocation {
  46. case ParamLocationQuery, ParamLocationUndefined:
  47. // We unescape undefined parameter locations here for older generated code,
  48. // since prior to this refactoring, they always query unescaped.
  49. value, err = url.QueryUnescape(value)
  50. if err != nil {
  51. return fmt.Errorf("error unescaping query parameter '%s': %v", paramName, err)
  52. }
  53. case ParamLocationPath:
  54. value, err = url.PathUnescape(value)
  55. if err != nil {
  56. return fmt.Errorf("error unescaping path parameter '%s': %v", paramName, err)
  57. }
  58. default:
  59. // Headers and cookies aren't escaped.
  60. }
  61. // If the destination implements encoding.TextUnmarshaler we use it for binding
  62. if tu, ok := dest.(encoding.TextUnmarshaler); ok {
  63. if err := tu.UnmarshalText([]byte(value)); err != nil {
  64. return fmt.Errorf("error unmarshalling '%s' text as %T: %s", value, dest, err)
  65. }
  66. return nil
  67. }
  68. // Everything comes in by pointer, dereference it
  69. v := reflect.Indirect(reflect.ValueOf(dest))
  70. // This is the basic type of the destination object.
  71. t := v.Type()
  72. if t.Kind() == reflect.Struct {
  73. // We've got a destination object, we'll create a JSON representation
  74. // of the input value, and let the json library deal with the unmarshalling
  75. parts, err := splitStyledParameter(style, explode, true, paramName, value)
  76. if err != nil {
  77. return err
  78. }
  79. return bindSplitPartsToDestinationStruct(paramName, parts, explode, dest)
  80. }
  81. if t.Kind() == reflect.Slice {
  82. // Chop up the parameter into parts based on its style
  83. parts, err := splitStyledParameter(style, explode, false, paramName, value)
  84. if err != nil {
  85. return fmt.Errorf("error splitting input '%s' into parts: %s", value, err)
  86. }
  87. return bindSplitPartsToDestinationArray(parts, dest)
  88. }
  89. // Try to bind the remaining types as a base type.
  90. return BindStringToObject(value, dest)
  91. }
  92. // This is a complex set of operations, but each given parameter style can be
  93. // packed together in multiple ways, using different styles of separators, and
  94. // different packing strategies based on the explode flag. This function takes
  95. // as input any parameter format, and unpacks it to a simple list of strings
  96. // or key-values which we can then treat generically.
  97. // Why, oh why, great Swagger gods, did you have to make this so complicated?
  98. func splitStyledParameter(style string, explode bool, object bool, paramName string, value string) ([]string, error) {
  99. switch style {
  100. case "simple":
  101. // In the simple case, we always split on comma
  102. parts := strings.Split(value, ",")
  103. return parts, nil
  104. case "label":
  105. // In the label case, it's more tricky. In the no explode case, we have
  106. // /users/.3,4,5 for arrays
  107. // /users/.role,admin,firstName,Alex for objects
  108. // in the explode case, we have:
  109. // /users/.3.4.5
  110. // /users/.role=admin.firstName=Alex
  111. if explode {
  112. // In the exploded case, split everything on periods.
  113. parts := strings.Split(value, ".")
  114. // The first part should be an empty string because we have a
  115. // leading period.
  116. if parts[0] != "" {
  117. return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", paramName)
  118. }
  119. return parts[1:], nil
  120. } else {
  121. // In the unexploded case, we strip off the leading period.
  122. if value[0] != '.' {
  123. return nil, fmt.Errorf("invalid format for label parameter '%s', should start with '.'", paramName)
  124. }
  125. // The rest is comma separated.
  126. return strings.Split(value[1:], ","), nil
  127. }
  128. case "matrix":
  129. if explode {
  130. // In the exploded case, we break everything up on semicolon
  131. parts := strings.Split(value, ";")
  132. // The first part should always be empty string, since we started
  133. // with ;something
  134. if parts[0] != "" {
  135. return nil, fmt.Errorf("invalid format for matrix parameter '%s', should start with ';'", paramName)
  136. }
  137. parts = parts[1:]
  138. // Now, if we have an object, we just have a list of x=y statements.
  139. // for a non-object, like an array, we have id=x, id=y. id=z, etc,
  140. // so we need to strip the prefix from each of them.
  141. if !object {
  142. prefix := paramName + "="
  143. for i := range parts {
  144. parts[i] = strings.TrimPrefix(parts[i], prefix)
  145. }
  146. }
  147. return parts, nil
  148. } else {
  149. // In the unexploded case, parameters will start with ;paramName=
  150. prefix := ";" + paramName + "="
  151. if !strings.HasPrefix(value, prefix) {
  152. return nil, fmt.Errorf("expected parameter '%s' to start with %s", paramName, prefix)
  153. }
  154. str := strings.TrimPrefix(value, prefix)
  155. return strings.Split(str, ","), nil
  156. }
  157. case "form":
  158. var parts []string
  159. if explode {
  160. parts = strings.Split(value, "&")
  161. if !object {
  162. prefix := paramName + "="
  163. for i := range parts {
  164. parts[i] = strings.TrimPrefix(parts[i], prefix)
  165. }
  166. }
  167. return parts, nil
  168. } else {
  169. parts = strings.Split(value, ",")
  170. prefix := paramName + "="
  171. for i := range parts {
  172. parts[i] = strings.TrimPrefix(parts[i], prefix)
  173. }
  174. }
  175. return parts, nil
  176. }
  177. return nil, fmt.Errorf("unhandled parameter style: %s", style)
  178. }
  179. // Given a set of values as a slice, create a slice to hold them all, and
  180. // assign to each one by one.
  181. func bindSplitPartsToDestinationArray(parts []string, dest interface{}) error {
  182. // Everything comes in by pointer, dereference it
  183. v := reflect.Indirect(reflect.ValueOf(dest))
  184. // This is the basic type of the destination object.
  185. t := v.Type()
  186. // We've got a destination array, bind each object one by one.
  187. // This generates a slice of the correct element type and length to
  188. // hold all the parts.
  189. newArray := reflect.MakeSlice(t, len(parts), len(parts))
  190. for i, p := range parts {
  191. err := BindStringToObject(p, newArray.Index(i).Addr().Interface())
  192. if err != nil {
  193. return fmt.Errorf("error setting array element: %s", err)
  194. }
  195. }
  196. v.Set(newArray)
  197. return nil
  198. }
  199. // Given a set of chopped up parameter parts, bind them to a destination
  200. // struct. The exploded parameter controls whether we send key value pairs
  201. // in the exploded case, or a sequence of values which are interpreted as
  202. // tuples.
  203. // Given the struct Id { firstName string, role string }, as in the canonical
  204. // swagger examples, in the exploded case, we would pass
  205. // ["firstName=Alex", "role=admin"], where in the non-exploded case, we would
  206. // pass "firstName", "Alex", "role", "admin"]
  207. //
  208. // We punt the hard work of binding these values to the object to the json
  209. // library. We'll turn those arrays into JSON strings, and unmarshal
  210. // into the struct.
  211. func bindSplitPartsToDestinationStruct(paramName string, parts []string, explode bool, dest interface{}) error {
  212. // We've got a destination object, we'll create a JSON representation
  213. // of the input value, and let the json library deal with the unmarshalling
  214. var fields []string
  215. if explode {
  216. fields = make([]string, len(parts))
  217. for i, property := range parts {
  218. propertyParts := strings.Split(property, "=")
  219. if len(propertyParts) != 2 {
  220. return fmt.Errorf("parameter '%s' has invalid exploded format", paramName)
  221. }
  222. fields[i] = "\"" + propertyParts[0] + "\":\"" + propertyParts[1] + "\""
  223. }
  224. } else {
  225. if len(parts)%2 != 0 {
  226. return fmt.Errorf("parameter '%s' has invalid format, property/values need to be pairs", paramName)
  227. }
  228. fields = make([]string, len(parts)/2)
  229. for i := 0; i < len(parts); i += 2 {
  230. key := parts[i]
  231. value := parts[i+1]
  232. fields[i/2] = "\"" + key + "\":\"" + value + "\""
  233. }
  234. }
  235. jsonParam := "{" + strings.Join(fields, ",") + "}"
  236. err := json.Unmarshal([]byte(jsonParam), dest)
  237. if err != nil {
  238. return fmt.Errorf("error binding parameter %s fields: %s", paramName, err)
  239. }
  240. return nil
  241. }
  242. // BindQueryParameter works much like BindStyledParameter, however it takes a query argument
  243. // input array from the url package, since query arguments come through a
  244. // different path than the styled arguments. They're also exceptionally fussy.
  245. // For example, consider the exploded and unexploded form parameter examples:
  246. // (exploded) /users?role=admin&firstName=Alex
  247. // (unexploded) /users?id=role,admin,firstName,Alex
  248. //
  249. // In the first case, we can pull the "id" parameter off the context,
  250. // and unmarshal via json as an intermediate. Easy. In the second case, we
  251. // don't have the id QueryParam present, but must find "role", and "firstName".
  252. // what if there is another parameter similar to "ID" named "role"? We can't
  253. // tell them apart. This code tries to fail, but the moral of the story is that
  254. // you shouldn't pass objects via form styled query arguments, just use
  255. // the Content parameter form.
  256. func BindQueryParameter(style string, explode bool, required bool, paramName string,
  257. queryParams url.Values, dest interface{}) error {
  258. // dv = destination value.
  259. dv := reflect.Indirect(reflect.ValueOf(dest))
  260. // intermediate value form which is either dv or dv dereferenced.
  261. v := dv
  262. // inner code will bind the string's value to this interface.
  263. var output interface{}
  264. if required {
  265. // If the parameter is required, then the generated code will pass us
  266. // a pointer to it: &int, &object, and so forth. We can directly set
  267. // them.
  268. output = dest
  269. } else {
  270. // For optional parameters, we have an extra indirect. An optional
  271. // parameter of type "int" will be *int on the struct. We pass that
  272. // in by pointer, and have **int.
  273. // If the destination, is a nil pointer, we need to allocate it.
  274. if v.IsNil() {
  275. t := v.Type()
  276. newValue := reflect.New(t.Elem())
  277. // for now, hang onto the output buffer separately from destination,
  278. // as we don't want to write anything to destination until we can
  279. // unmarshal successfully, and check whether a field is required.
  280. output = newValue.Interface()
  281. } else {
  282. // If the destination isn't nil, just use that.
  283. output = v.Interface()
  284. }
  285. // Get rid of that extra indirect as compared to the required case,
  286. // so the code below doesn't have to care.
  287. v = reflect.Indirect(reflect.ValueOf(output))
  288. }
  289. // This is the basic type of the destination object.
  290. t := v.Type()
  291. k := t.Kind()
  292. switch style {
  293. case "form":
  294. var parts []string
  295. if explode {
  296. // ok, the explode case in query arguments is very, very annoying,
  297. // because an exploded object, such as /users?role=admin&firstName=Alex
  298. // isn't actually present in the parameter array. We have to do
  299. // different things based on destination type.
  300. values, found := queryParams[paramName]
  301. var err error
  302. switch k {
  303. case reflect.Slice:
  304. // In the slice case, we simply use the arguments provided by
  305. // http library.
  306. if !found {
  307. if required {
  308. return fmt.Errorf("query parameter '%s' is required", paramName)
  309. } else {
  310. // If an optional parameter is not found, we do nothing,
  311. return nil
  312. }
  313. }
  314. err = bindSplitPartsToDestinationArray(values, output)
  315. case reflect.Struct:
  316. // This case is really annoying, and error prone, but the
  317. // form style object binding doesn't tell us which arguments
  318. // in the query string correspond to the object's fields. We'll
  319. // try to bind field by field.
  320. var fieldsPresent bool
  321. fieldsPresent, err = bindParamsToExplodedObject(paramName, queryParams, output)
  322. // If no fields were set, and there is no error, we will not fall
  323. // through to assign the destination.
  324. if !fieldsPresent {
  325. return nil
  326. }
  327. default:
  328. // Primitive object case. We expect to have 1 value to
  329. // unmarshal.
  330. if len(values) == 0 {
  331. if required {
  332. return fmt.Errorf("query parameter '%s' is required", paramName)
  333. } else {
  334. return nil
  335. }
  336. }
  337. if len(values) != 1 {
  338. return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
  339. }
  340. if !found {
  341. if required {
  342. return fmt.Errorf("query parameter '%s' is required", paramName)
  343. } else {
  344. // If an optional parameter is not found, we do nothing,
  345. return nil
  346. }
  347. }
  348. err = BindStringToObject(values[0], output)
  349. }
  350. if err != nil {
  351. return err
  352. }
  353. // If the parameter is required, and we've successfully unmarshaled
  354. // it, this assigns the new object to the pointer pointer.
  355. if !required {
  356. dv.Set(reflect.ValueOf(output))
  357. }
  358. return nil
  359. } else {
  360. values, found := queryParams[paramName]
  361. if !found {
  362. if required {
  363. return fmt.Errorf("query parameter '%s' is required", paramName)
  364. } else {
  365. return nil
  366. }
  367. }
  368. if len(values) != 1 {
  369. return fmt.Errorf("parameter '%s' is not exploded, but is specified multiple times", paramName)
  370. }
  371. parts = strings.Split(values[0], ",")
  372. }
  373. var err error
  374. switch k {
  375. case reflect.Slice:
  376. err = bindSplitPartsToDestinationArray(parts, output)
  377. case reflect.Struct:
  378. err = bindSplitPartsToDestinationStruct(paramName, parts, explode, output)
  379. default:
  380. if len(parts) == 0 {
  381. if required {
  382. return fmt.Errorf("query parameter '%s' is required", paramName)
  383. } else {
  384. return nil
  385. }
  386. }
  387. if len(parts) != 1 {
  388. return fmt.Errorf("multiple values for single value parameter '%s'", paramName)
  389. }
  390. err = BindStringToObject(parts[0], output)
  391. }
  392. if err != nil {
  393. return err
  394. }
  395. if !required {
  396. dv.Set(reflect.ValueOf(output))
  397. }
  398. return nil
  399. case "deepObject":
  400. if !explode {
  401. return errors.New("deepObjects must be exploded")
  402. }
  403. return UnmarshalDeepObject(dest, paramName, queryParams)
  404. case "spaceDelimited", "pipeDelimited":
  405. return fmt.Errorf("query arguments of style '%s' aren't yet supported", style)
  406. default:
  407. return fmt.Errorf("style '%s' on parameter '%s' is invalid", style, paramName)
  408. }
  409. }
  410. // bindParamsToExplodedObject reflects the destination structure, and pulls the value for
  411. // each settable field from the given parameters map. This is to deal with the
  412. // exploded form styled object which may occupy any number of parameter names.
  413. // We don't try to be smart here, if the field exists as a query argument,
  414. // set its value. This function returns a boolean, telling us whether there was
  415. // anything to bind. There will be nothing to bind if a parameter isn't found by name,
  416. // or none of an exploded object's fields are present.
  417. func bindParamsToExplodedObject(paramName string, values url.Values, dest interface{}) (bool, error) {
  418. // Dereference pointers to their destination values
  419. binder, v, t := indirect(dest)
  420. if binder != nil {
  421. _, found := values[paramName]
  422. if !found {
  423. return false, nil
  424. }
  425. return true, BindStringToObject(values.Get(paramName), dest)
  426. }
  427. if t.Kind() != reflect.Struct {
  428. return false, fmt.Errorf("unmarshalling query arg '%s' into wrong type", paramName)
  429. }
  430. fieldsPresent := false
  431. for i := 0; i < t.NumField(); i++ {
  432. fieldT := t.Field(i)
  433. // Skip unsettable fields, such as internal ones.
  434. if !v.Field(i).CanSet() {
  435. continue
  436. }
  437. // Find the json annotation on the field, and use the json specified
  438. // name if available, otherwise, just the field name.
  439. tag := fieldT.Tag.Get("json")
  440. fieldName := fieldT.Name
  441. if tag != "" {
  442. tagParts := strings.Split(tag, ",")
  443. name := tagParts[0]
  444. if name != "" {
  445. fieldName = name
  446. }
  447. }
  448. // At this point, we look up field name in the parameter list.
  449. fieldVal, found := values[fieldName]
  450. if found {
  451. if len(fieldVal) != 1 {
  452. return false, fmt.Errorf("field '%s' specified multiple times for param '%s'", fieldName, paramName)
  453. }
  454. err := BindStringToObject(fieldVal[0], v.Field(i).Addr().Interface())
  455. if err != nil {
  456. return false, fmt.Errorf("could not bind query arg '%s' to request object: %s'", paramName, err)
  457. }
  458. fieldsPresent = true
  459. }
  460. }
  461. return fieldsPresent, nil
  462. }
  463. // indirect
  464. func indirect(dest interface{}) (interface{}, reflect.Value, reflect.Type) {
  465. v := reflect.ValueOf(dest)
  466. if v.Type().NumMethod() > 0 && v.CanInterface() {
  467. if u, ok := v.Interface().(Binder); ok {
  468. return u, reflect.Value{}, nil
  469. }
  470. }
  471. v = reflect.Indirect(v)
  472. t := v.Type()
  473. // special handling for custom types which might look like an object. We
  474. // don't want to use object binding on them, but rather treat them as
  475. // primitive types. time.Time{} is a unique case since we can't add a Binder
  476. // to it without changing the underlying generated code.
  477. if t.ConvertibleTo(reflect.TypeOf(time.Time{})) {
  478. return dest, reflect.Value{}, nil
  479. }
  480. if t.ConvertibleTo(reflect.TypeOf(types.Date{})) {
  481. return dest, reflect.Value{}, nil
  482. }
  483. return nil, v, t
  484. }