connexion icon indicating copy to clipboard operation
connexion copied to clipboard

Array query parameters using square brackets cause error

Open jhaislip opened this issue 5 years ago • 4 comments

Description

A query parameter representing an array of strings is parsed incorrectly by the deep object processing in the OpenAPIURIParser.

Expected behaviour

For example, if the path /users/{user_id} has the parameter:

expand:
      in: query
      name: expand
      required: false
      schema:
        type: array
        items:
          type: string
      description: List of fields which should be expanded in the response. 

The request URI https://api.example.com/users/123?expand[]=user.profile&expand[]=user.address should pass the array of strings ['user.profile', 'user.address'] to the expand argument of the handler.

Actual behaviour

An TypeError: sequence item 0: expected str instance, dict found exception is thrown on line 237 of decorators/uri_parsing.py. This is due to the _preprocess_deep_objects method of the OpenAPIURIParser misinterpreting the square brackets and periods for nested notation and converting the query_data from {'expand[]': ['user.profile', 'user.address']} to {'expand': [{'': 'user.profile'}]}

Steps to reproduce

Make a path which accepts a query parameter as described above. Send a request to the path which uses repeated-key/square-bracket notation for the query parameter.

Additional info:

I would attempt a PR but I'm not familiar enough with the code base. I was not aware that Connexion had support for nested objects in query parameters so I'm not sure how it should be distinguished from the situation I've described above. I also didn't see a way to disable it in the code.

Output of the commands:

  • python --version Python 3.8.5
  • pip show connexion | grep "^Version\:" Version: 2.7.0

[Update] It looks like the notation for array-based query params isn't standardized. I was let axios serializing my query params which results in the square bracket notation described above. I was able to customize the param serialization to use the qs library and disabled square brackets. This is a workaround if this issue is not considered a bug.

jhaislip avatar Jul 31 '20 18:07 jhaislip

Bump. This is a pretty egregious bug. Both ?ids[]=1&ids[]=2 and ?ids[0]=1&ids[1]=2 are pretty standard ways of passing around multiple values for a single param. This is the default behavior for a number of http libraries in various languages.

dursk avatar Jan 22 '21 19:01 dursk

For axios case, if you switch to qs then json objects passed as string won't work.

aykutkilic avatar Apr 07 '21 23:04 aykutkilic

The OpenAPI spec defines serialization formats for arrays in the query string. ?ids[0]=1&ids[1]=2 does not seem to be supported. ?ids[]=1&ids[]=2 seems to be the default with a parameter name of ids[].

RobbeSneyders avatar Jul 28 '21 15:07 RobbeSneyders

This issue seems to come from uri_parsing.py :

    def _make_deep_object(self, k, v):
        """ consumes keys, value pairs like (a[foo][bar], "baz")
            returns (a, {"foo": {"bar": "baz"}}}, is_deep_object)
        """
        root_key = None
        if k in self.param_schemas.keys():
            return k, v, False
        else:
            for keys in self.param_schemas.keys():
                if k.startswith(keys):
                    rest = keys.replace(k, '')
                    root_key = rest

In my case I was able to quickly avoid this issue by adding a similar parameters (or just replacing the old one). e.g.

expand:
      in: query
      name: expand
      required: false
      schema:
        type: array
        items:
          type: string
      description: List of fields which should be expanded in the response. 
expand[]:
      in: query
      name: expand
      required: false
      schema:
        type: array
        items:
          type: string
      description: List of fields which should be expanded in the response. 

pmerienne avatar Feb 25 '22 14:02 pmerienne

Fixed in #1408

RobbeSneyders avatar Feb 19 '23 09:02 RobbeSneyders