connexion icon indicating copy to clipboard operation
connexion copied to clipboard

Error trying to upload file through POST with multipart/mixed

Open KlamerS opened this issue 3 years ago • 1 comments

Description

When trying to upload an image file, with a POST using multipart/mixed, I see a TypeError, reported as an internal server error.

Besides fixing this internal server error I am also interested in a work-around to get a connexion to upload a file.

Expected behaviour

Expected behaviour is that the function postMatch_Sessions shall be called.

Actual behaviour

An error in the server while trying to retrieve the information from the body of the POST action.

Steps to reproduce

Using the following files: zz/x.yaml:

openapi: 3.0.0
servers:
  - description: is this needed
    url: http://localhost:8080/sacei/0.0.1
info: 
  description: SACEI API
  version: 0.0.1
  title: SACEI
paths:
  /Match_Sessions:
    post:
      description: start a new session
      operationId: sacei.postMatch_Sessions
      responses:
        '201':
          description: item created
      requestBody:
        content:
          multipart/mixed:
            schema:
              type: object
              properties:
                ifov:
                  description: IFOV in radians
                  type: number
                image:
                  type: string
                  format: binary
            encoding:
              image:
                contentType: image/png, image/jpeg

sacei.py:

def postMatch_Sessions():
    print('postMatch_Sessions')

test.py:

import connexion

a = connexion.App('name', specification_dir='zz/')
a.add_api('x.yaml')

a.run(port=8080)

Run test.py, and running the command (from outside the docker -- translating port 8080 to 11091):

> curl -X 'POST'   'http://localhost:11091/sacei/0.0.1/Match_Sessions'  -H 'Content-Type: multipart/mixed' -d '{"ifov":0.1, "image":""}'

this commands returns:

{
  "detail": "The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.",
  "status": 500,
  "title": "Internal Server Error",
  "type": "about:blank"
}

and the output of the test.py program is:

* Running on http://172.27.0.2:8080/ (Press CTRL+C to quit)
[2022-02-09 16:02:19,945] ERROR in app: Exception on /sacei/0.0.1/Match_Sessions [POST]
Traceback (most recent call last):
  File "/opt/conda/lib/python3.8/site-packages/Flask-2.0.2-py3.8.egg/flask/app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "/opt/conda/lib/python3.8/site-packages/Flask-2.0.2-py3.8.egg/flask/app.py", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/opt/conda/lib/python3.8/site-packages/Flask-2.0.2-py3.8.egg/flask/app.py", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "/opt/conda/lib/python3.8/site-packages/Flask-2.0.2-py3.8.egg/flask/app.py", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/decorators/decorator.py", line 68, in wrapper
    response = function(request)
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/decorators/uri_parsing.py", line 149, in wrapper
    response = function(request)
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/decorators/validation.py", line 193, in wrapper
    response = function(request)
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/decorators/parameter.py", line 97, in wrapper
    operation.get_arguments(request.path_params, query, request_body,
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/operations/abstract.py", line 284, in get_arguments
    ret.update(self._get_body_argument(body, arguments,
  File "/opt/conda/lib/python3.8/site-packages/connexion-2020.0.dev1-py3.8.egg/connexion/operations/openapi.py", line 296, in _get_body_argument
    body_arg.update(body or {})
TypeError: cannot convert dictionary update sequence element #0 to a sequence
172.27.0.1 - - [09/Feb/2022 16:02:19] "POST /sacei/0.0.1/Match_Sessions HTTP/1.1" 500 -

Additional info:

Output of the commands:

  • python --version: 3.8.8
  • pip show connexion | grep "^Version\:" : Version: 2020.0.dev1 (== today's github version) (similar error message when using version 2.6.0)

KlamerS avatar Feb 09 '22 16:02 KlamerS

@KlamerS , the 2.12.0 release that came out today contains some recent fixes to multipart/mixed bodies for aiohttp. However, I see you're using flask and this issue may be a duplicate report of #992

ddurham2 avatar Feb 24 '22 03:02 ddurham2

multipart/mixed is not supported by either Flask/werkzeug or starlette, which connexion is built on. You can either use multipart/form-data, or parse the multipart/mixed yourself by reading the stream on the request object yourself.

RobbeSneyders avatar Feb 20 '23 22:02 RobbeSneyders

Since it is apparently part of the openapi spec, to be spec compliant, isn't it therefore incumbent on connexion to do as you suggest and parse the stream itself?

ddurham2 avatar Feb 20 '23 23:02 ddurham2

In theory it would be great to support it. But multipart/mixed is so complex and support across the Python ecosystem lacking that it's not feasible.

RobbeSneyders avatar Feb 21 '23 08:02 RobbeSneyders