I recently needed a Swagger 2.0 API endpoint to receive a file upload, and Swagger UI wouldn't show the file input button until I declared it correctly.
Maybe this should go into the docs, but until I find the time to do it properly (and get the hang of .rst files again, as I always forget the syntax), I'll post my problem and how I solved it here. Just so that others having the same problem can find it in search engines, at least.
And then, in the endpoint that I want to declare as receiving a file:
# Import installed packages
import uuid
from flask import abort
from webargs import fields
from flask_apispec import doc, use_kwargs, marshal_with
from flask_jwt_extended import jwt_required, get_current_user
# Import app code
from app.main import app
from app.api.api_v1.api_docs import docs, security_params
from app.core import config
from app.utils.utils import get_message_from_image
from app.db.utils import check_if_user_is_superuser, check_if_user_is_active
from app.api.api_v1.api_docs import FileField
# For type hints
from werkzeug.datastructures import FileStorage
# Import Schemas
from app.schemas.msg import MsgSchema
@docs.register
@doc(description="Get file and to stuff", security=security_params, tags=["files"], consumes=['multipart/form-data'])
@app.route(f"{config.API_V1_STR}/upload/", methods=["POST"])
@use_kwargs({"image": FileField(required=True)}, locations=["files"])
@marshal_with(MsgSchema())
@jwt_required
def route_files_post(image: FileStorage):
user = get_current_user()
if not check_if_user_is_active(user):
abort(400, "User not active")
elif not check_if_user_is_superuser(user):
abort(400, "User not a superuser")
file_id = uuid.uuid4()
file_path = f"/data/files/{file_id}"
image.save(file_path)
message = get_message_from_image(file_path)
return ({"msg": message}, 200)
Notice that I'm using Python 3.6 type hints in the image: FileStorage. This is just to have code completion in the editor.
But also, notice that what it receives is not a bytes object, nor a file object, it's a werkzeug's FileStorage object, that includes a save method.
That's the complete example that did the trick for me.
Also, if you think this is something that would be worthwhile having integrated into Flask-apispec / apispec / Webargs / Marshmallow (actually I don't know where would it be), let me know and I'll do my best to submit a PR.
I desperately need to both upload a file to the API, and for that upload method to be self-documenting. I'm using the swagger-ui in lieu of a proper UI.
I recently needed a Swagger 2.0 API endpoint to receive a file upload, and Swagger UI wouldn't show the file input button until I declared it correctly.
Maybe this should go into the docs, but until I find the time to do it properly (and get the hang of .rst files again, as I always forget the syntax), I'll post my problem and how I solved it here. Just so that others having the same problem can find it in search engines, at least.
I'm cross-posting from this comment, as this is actually more relevant to Flask-apispec.
My project is based on https://github.com/tiangolo/full-stack-flask-couchdb (Flask-apispec).
I had to "create an apispec MarshmallowPlugin"
After creating the plugin, add it to the
APISpec
instance.And then, AFTER that, create a custom
FileField
.This is how the file where I set up Flask-apispec looks like.
./app/api/api_v1/api_docs.py
:And then, in the endpoint that I want to declare as receiving a file:
Notice that I'm using Python 3.6 type hints in the
image: FileStorage
. This is just to have code completion in the editor.But also, notice that what it receives is not a bytes object, nor a file object, it's a werkzeug's
FileStorage
object, that includes asave
method.That's the complete example that did the trick for me.
Also, if you think this is something that would be worthwhile having integrated into Flask-apispec / apispec / Webargs / Marshmallow (actually I don't know where would it be), let me know and I'll do my best to submit a PR.