Open 0xbart opened 1 year ago
Nasty hack to bypass this issue is by intercepting the SwaggerView:
First the Custom API class:
from flask_restx import Api
class CustomApi(Api):
def __init__(self):
super().__init__(
**{
"version": config.API_VERSION,
"title": config.API_TITLE,
"doc": config.API_DOC_URL,
"description": config.API_DESCRIPTION,
"authorizations": authorization,
"security": ["x-api-key", "bearer"],
}
)
def _register_specs(self, app_or_blueprint):
if self._add_specs:
endpoint = str("specs")
self._register_view(
app_or_blueprint,
CustomSwaggerView, # <---- link this to the new custom class (see below)
self.default_namespace,
f"{config.API_DOC_URL}/swagger.json",
endpoint=endpoint,
resource_class_args=(self,),
)
self.endpoints.add(endpoint)
@property
def specs_url(self):
# overwrite custom swagger json url
# please do not add '/' after API_DOC_URL, to prevent //swagger.json url style
return f"{self.base_url[:-1]}{config.API_DOC_URL}swagger.json"
Furthermore, the Custom Swagger class:
from flask_restx.api import SwaggerView
class CustomSwaggerView(SwaggerView):
"""
Use custom swagger view to support different type of response, instead of the default application/json.
This is required because the generated Typescript code by parsing the Swagger json is using json for all requests.
Some API calls require type blob instead of json, such as: QRcode PNG setup.
"""
def get(self):
schema = self.api.__schema__
# Check if schema contains any errors. If errors, do not try to manipulate
# the dict and return created schema instead.
if "error" in schema:
return schema, 500
for ns in self.api.namespaces:
for resource, urls, route_doc, kwargs in ns.resources:
# check if resource has value 'custom_swagger' set.
# custom swagger could be set via the following attribute (passing to the Resource class):
#
# custom_swagger = {
# "get": {
# "responses": {"200": {"description": "Success", "schema": {"type": "file"}}},
# "produces": ["image/png"],
# }
# }
custom_swagger = getattr(resource, "custom_swagger", None)
if custom_swagger:
# if custom swagger, loop through all associated URLs and update dict
for url in self.api.ns_urls(ns, urls):
for method, method_values in custom_swagger.items():
if method.lower() not in schema["paths"][url]:
continue
for key, value in method_values.items():
if key == 'responses':
schema["paths"][url][method.lower()][key].update(value)
else:
schema["paths"][url][method.lower()][key] = value
return schema, 200
Now lets create an example API call which returns an PNG instead of json:
from flask_restx import Resource
class ExampleResource(Resource):
custom_swagger = {
"get": {
"responses": {"200": {"description": "Success", "schema": {"type": "file"}}},
"produces": ["image/png"],
}
}
@api.route('/')
def get(self):
# implement me
pass
How do i define a function to responds with a binary file instead of json? This because typescript auto generated api documentation and does not use 'type: blob' on this api call.
Code
Expected Behavior
Swagger example: https://swagger.io/docs/specification/describing-responses/#response-that-returns-a-file (3.0) or https://swagger.io/docs/specification/2-0/describing-responses/ (2.0)
Actual Behavior
Swagger example: