Skip to content

[Bug]: Unable to send files through multipart/form in Flask when integrated with Openapi-Core #630

Closed
@rohan-97

Description

@rohan-97

Actual Behavior

Hello,

I am trying to Implement an API,
The API Takes a file as input in a REST API using multipart/form

I am using Flask to implement the REST API and following is the flask script

#!/usr/bin/python3
"""Test server."""

import os
import random
from flask import Flask, request, jsonify
from openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator
from openapi_core import Spec

SPEC = "file_reader.yaml"
openapi = FlaskOpenAPIViewDecorator.from_spec(Spec.from_file_path(SPEC))

app = Flask(__name__)

def __save_file_locally(file_object:object) -> str:
    if file_object.filename:
        filepath = os.path.join("/tmp/", f"{file_object.filename}.{random.randint(1,100)}")
        if os.path.exists(filepath):
            os.unlink(filepath)
        file_object.save(filepath)
        return filepath
    return None

@app.route("/test", methods=["POST"])
@openapi
def read_permission():
    """Test function"""
    # print(f"request OpenAPI dir : {request.openapi.body.items()}")
    __save_file_locally(request.files['inputFile'])
    return jsonify({"key": "Value"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=567, debug=True)

    # curl -X POST -H  "Content-type: application/json" --data '{"flag":"ttF"}' http://localhost:567/test

and following is the file_reader.yaml file

openapi: '3.0.2'
info:
  title: Test Title
  version: '1.0'
servers:
  - url: http://localhost:567/
paths:
  /test:
    post:
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                inputFile:
                  type: string 
                  format: binary
      responses:
        200:
          description: Sample response
          content:
            application/json:
              schema:
                type: object
                properties:
                  key:
                    type: string
                    minLength: 6
                    maxLength: 20

# curl -X POST -H  "Content-type: multipart/form-data" -F "file=@file_reader.py" http://localhost:567/test

However when I hit the API I get following validation error

root@ip-10-31-1-221:~/openapi_core_POC# curl -X POST -H  "Content-type: multipart/form-data" -F "file=@file_reader.py" http://localhost:567/test | python3 -m json.tool
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2884  100  1588  100  1296   258k   210k --:--:-- --:--:-- --:--:--  469k
{
    "errors": [
        {
            "class": "<class 'openapi_core.deserializing.media_types.exceptions.MediaTypeDeserializeError'>",
            "status": 400,
            "title": "Failed to deserialize value with multipart/form-data mimetype: --------------------------2c4bf22387dcbcbc\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file_reader.py\"\r\nContent-Type: application/octet-stream\r\n\r\n#!/usr/bin/python3\n\"\"\"Test server.\"\"\"\n\nimport os\nimport random\nfrom flask import Flask, request, jsonify\nfrom openapi_core.contrib.flask.decorators import FlaskOpenAPIViewDecorator\nfrom openapi_core import Spec\n\nSPEC = \"file_reader.yaml\"\nopenapi = FlaskOpenAPIViewDecorator.from_spec(Spec.from_file_path(SPEC))\n\napp = Flask(__name__)\n\ndef __save_file_locally(file_object:object) -> str:\n    if file_object.filename:\n        filepath = os.path.join(\"/tmp/\", f\"{file_object.filename}.{random.randint(1,100)}\")\n        if os.path.exists(filepath):\n            os.unlink(filepath)\n        file_object.save(filepath)\n        return filepath\n    return None\n\n@app.route(\"/test\", methods=[\"POST\"])\n@openapi\ndef read_permission():\n    \"\"\"Test function\"\"\"\n    # print(f\"request OpenAPI dir : {request.openapi.body.items()}\")\n    __save_file_locally(request.files['inputFile'])\n    return jsonify({\"key\": \"Value\"})\n\nif __name__ == \"__main__\":\n    app.run(host=\"0.0.0.0\", port=567, debug=True)\n\n    # curl -X POST -H  \"Content-type: application/json\" --data '{\"flag\":\"ttF\"}' http://localhost:567/test\n\r\n--------------------------2c4bf22387dcbcbc--\r\n"
        }
    ]
}

Expected Behavior

It is expected that there should be no validation errors as we have specified mime type as multipart/form.

Also as per OpenAPI docs, OpenAPI 3.0 does not support type:file field,
and as per docs, while taking input we should specify type:string and format:binary

so just wanted to confirm whether the request body is correct or not

      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                inputFile:
                  type: string 
                  format: binary

Steps to Reproduce

Write the provided python3 file as file_reader.py and provided yaml file as file_reader.yaml

Execute file_reader.py file
and use following CURL command to make a request

curl -X POST -H  "Content-type: multipart/form-data" -F "file=@file_reader.py" http://localhost:567/test

OpenAPI Core Version

0.17.1

OpenAPI Core Integration

Flask

Affected Area(s)

Deserializing

References

No response

Anything else we need to know?

No response

Would you like to implement a fix?

Yes

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugIndicates an issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy