Open blondowski opened 6 years ago
EDIT: On reflection, perhaps I've misunderstood. I thought you wanted to change where assets were being delivered from, which might be possible with what I've written below. But it seems that instead your blueprint is not registering at the desired URL.
Perhaps there's an answer in here: https://flask-restplus.readthedocs.io/en/stable/scaling.html#multiple-apis-with-reusable-namespaces
Original response below.
You may want to try something like the following:
from flask import render_template
from bs4 import BeautifulSoup
import werkzeug.exceptions
def custom_ui(self):
"""Override for custom UI"""
if self._doc_view:
return self._doc_view()
elif not self._doc:
self.abort(werkzeug.exceptions.NotFound)
r = render_template("swagger-ui.html", title=self.title, specs_url=self.specs_url)
soup = BeautifulSoup(r, 'lxml')
# do things with BeautifulSoup here
return str(soup)
Api.render_doc = custom_ui
api = Api(blueprint, doc='/doc/)
It's briefly mentioned in the docs. I cobbled together the above after a spot of Googling. I'm sure it could be much improved.
Thanks. Yes, I do want to change where the assets are delivered from: instead of /swaggerui/bower/swagger-ui/dist/fonts/DroidSans-Bold.ttf they need to come from: /api/swaggerui/bower/swagger-ui/dist/fonts/DroidSans-Bold.ttf
Our load balancer doesn't know how to route /swaggerui.
Hi @blondowski, Did you manage to resolve your issue? It seems that I've got the same one on my side and haven't find any good solution yet.
Thanks in advance!
No sir/mam. I've tried multiple things and couldn't get it to work.
I am having the same issue here. swaggerui
redirects to hostname withouth url_prefix
I've found a little bit "hacky" work around. To fix this issue you need to derive from Api
class and override _register_apidoc
method which will register your custom apidoc.Apidoc
definition.
With below snippet, SwaggerUI is served under /api/service/v1
path:
class MyCustomApi(Api):
def _register_apidoc(self, app: Flask) -> None:
conf = app.extensions.setdefault('restplus', {})
custom_apidoc = apidoc.Apidoc('restplus_doc', 'flask_restplus.apidoc',
template_folder='templates', static_folder='static',
static_url_path='/api/service/v1')
@custom_apidoc.add_app_template_global
def swagger_static(filename: str) -> str:
return url_for('restplus_doc.static', filename=filename)
if not conf.get('apidoc_registered', False):
app.register_blueprint(custom_apidoc)
conf['apidoc_registered'] = True
blueprint = Blueprint('my_blueprint', __name__, url_prefix='/api/service/v1')
api = MyCustomApi(blueprint, version='1.0', title='My Custom API', description='My Custom API.',
validate=True)
with this I can change the path but still cant find the static files.
Strange... I tested it today with Nginx as a reverse proxy without any changes in configuration. Maybe this comment (https://github.com/noirbizarre/flask-restplus/issues/223#issuecomment-381439513) will help you somehow?
Also, be sure to clear cache in your browser - I've cached myself on that :)
My environment is strange. we are using haproxy
and I don't have access to it and our urls are like http://something.something:portnumber/<app_name>/<app_routes>
. Originally it changed the static path to http://something.something:portnumber/<app_routes>
which removed the <app_name>
. now path looks OK but there is nothing there!
Play with swagger_static
function an try to find proper filename
for you:
@custom_apidoc.add_app_template_global
def swagger_static(filename: str) -> str:
return url_for('restplus_doc.static', filename=filename)
Also, try a solution from this comment: https://github.com/noirbizarre/flask-restplus/issues/262#issuecomment-290065151.
After lots of trials and errors I managed to fix the static
paths right, now I get 404
for swagger.json
is there a way to specify the path for that file?
I think this one may help you: https://github.com/noirbizarre/flask-restplus/issues/223#issuecomment-381439513. But I'm not sure about it... 🤔
Didn't work. It is so frustrating! I can change all the paths but as soon as I do I loose track of the satics.
I think the problem is in the api.py in this method
def _register_apidoc(self, app):
conf = app.extensions.setdefault('restplus', {})
if not conf.get('apidoc_registered', False):
app.register_blueprint(apidoc.apidoc)
conf['apidoc_registered'] = True
What is missing is the url_prefix; it's not used here and you also cannot specify one. Basically when I change my REST API to start with something like /api/v1 ... I can manage to have /api/v1/doc to present the Swagger UI but those settings are not taken into account for that method shown above.
I don't like the idea to hack something so the right implementation is appreciated ...
Any updates on this?
Has this been prioritized, the workaround is not ideal
did anyone get any hack or which stage the implementation is?
I did some hack and worked on Kubernetes + Istio
@property
def specs_url(self):
'''
The Swagger specifications absolute url (ie. `swagger.json`)
:rtype: str
'''
return url_for(self.endpoint('specs'), _external=False)
I add # 223
and this and worked:
I've found a little bit "hacky" work around. To fix this issue you need to derive from
Api
class and override_register_apidoc
method which will register your customapidoc.Apidoc
definition.With below snippet, SwaggerUI is served under
/api/service/v1
path:class MyCustomApi(Api): def _register_apidoc(self, app: Flask) -> None: conf = app.extensions.setdefault('restplus', {}) custom_apidoc = apidoc.Apidoc('restplus_doc', 'flask_restplus.apidoc', template_folder='templates', static_folder='static', static_url_path='/api/service/v1') @custom_apidoc.add_app_template_global def swagger_static(filename: str) -> str: return url_for('restplus_doc.static', filename=filename) if not conf.get('apidoc_registered', False): app.register_blueprint(custom_apidoc) conf['apidoc_registered'] = True blueprint = Blueprint('my_blueprint', __name__, url_prefix='/api/service/v1') api = MyCustomApi(blueprint, version='1.0', title='My Custom API', description='My Custom API.', validate=True)
I did some hack and worked on Kubernetes + Istio
@property def specs_url(self): ''' The Swagger specifications absolute url (ie. `swagger.json`) :rtype: str ''' return url_for(self.endpoint('specs'), _external=False)
I add # 223
and this and worked:
I've found a little bit "hacky" work around. To fix this issue you need to derive from
Api
class and override_register_apidoc
method which will register your customapidoc.Apidoc
definition. With below snippet, SwaggerUI is served under/api/service/v1
path:class MyCustomApi(Api): def _register_apidoc(self, app: Flask) -> None: conf = app.extensions.setdefault('restplus', {}) custom_apidoc = apidoc.Apidoc('restplus_doc', 'flask_restplus.apidoc', template_folder='templates', static_folder='static', static_url_path='/api/service/v1') @custom_apidoc.add_app_template_global def swagger_static(filename: str) -> str: return url_for('restplus_doc.static', filename=filename) if not conf.get('apidoc_registered', False): app.register_blueprint(custom_apidoc) conf['apidoc_registered'] = True blueprint = Blueprint('my_blueprint', __name__, url_prefix='/api/service/v1') api = MyCustomApi(blueprint, version='1.0', title='My Custom API', description='My Custom API.', validate=True)
Gives me 500 Server Error
Any updates on this? I'm having the same issue
It seems to me that flask_restplus is lacking a mechanism to correctly set the url_prefix when registering the apidoc
blueprint here: https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/api.py#L243.
A workaround, that appears to work for my current use case at least, is to monkey patch flask_restplus.apidoc.apidoc
by setting the required url_prefix
directly on the module level object here: https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/apidoc.py#L21.
The below snippet will serve the documentation at /api/doc
and the swagger UI resources at /api/swaggerui
:
from flask import Flask, Blueprint
from flask_restplus import Api
# Import apidoc for monkey patching
from flask_restplus.apidoc import apidoc
URL_PREFIX = '/api'
# Make a global change setting the URL prefix for the swaggerui at the module level
apidoc.url_prefix = URL_PREFIX
app = Flask(__name__)
blueprint = Blueprint('api', 'blueprint_name', url_prefix=URL_PREFIX)
api = Api(blueprint, doc='/doc/')
app.register_blueprint(blueprint)
It seems to me that flask_restplus is lacking a mechanism to correctly set the url_prefix when registering the
apidoc
blueprint here: https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/api.py#L243.A workaround, that appears to work for my current use case at least, is to monkey patch
flask_restplus.apidoc.apidoc
by setting the requiredurl_prefix
directly on the module level object here: https://github.com/noirbizarre/flask-restplus/blob/master/flask_restplus/apidoc.py#L21.The below snippet will serve the documentation at
/api/doc
and the swagger UI resources at/api/swaggerui
:from flask import Flask, Blueprint from flask_restplus import Api # Import apidoc for monkey patching from flask_restplus.apidoc import apidoc URL_PREFIX = '/api' # Make a global change setting the URL prefix for the swaggerui at the module level apidoc.url_prefix = URL_PREFIX app = Flask(__name__) blueprint = Blueprint('api', 'blueprint_name', url_prefix=URL_PREFIX) api = Api(blueprint, doc='/doc/') app.register_blueprint(blueprint)
This one is working for fix the url path to swagger.json and swaggerui folder. Thanks @amacd31 For make it work behind 1 or 2 proxy's with HTTPS is another story.
I've made a "minimal" (kinda) gist here to try to collate some of the approaches listed here, all into one place. I've tried to cover:
url_prefix
https://gist.github.com/austinjp/c0c3ed361fd54ed4faf0065eb40502eb
Does it fail for anyone?
I've used @austinjp gist but had to add the not-so-pretty hack @giovanniattina found and it finally worked in a k8s deployment served throughout HTTPS, here's an extract from my test code: Note: you might want to lose the CORS decorator and replace the CONTEXT_PATH configuration with your own (probably better) way to get it.
csrf_protect = CSRFProtect()
def _register_apidoc(self, app: Flask) -> None:
conf = app.extensions.setdefault('restplus', {})
custom_apidoc = apidoc.Apidoc('restplus_doc', 'flask_restplus.apidoc',
template_folder='templates', static_folder='static',
static_url_path="{context_path}/api/v1".format(context_path=settings.CONTEXT_PATH))
@custom_apidoc.add_app_template_global
def swagger_static(filename: str) -> str:
return url_for('restplus_doc.static', filename=filename)
if not conf.get('apidoc_registered', False):
app.register_blueprint(custom_apidoc)
conf['apidoc_registered'] = True
def api_patches(api_blueprint):
Api._register_apidoc = _register_apidoc
@property
def fix_specs_url(self):
return url_for(self.endpoint('specs'), _external=False)
Api.specs_url = fix_specs_url
api_fixed = Api(
api_blueprint,
title="le title",
description="le description",
version="0.1.0", doc="/docs", decorators=[csrf_protect.exempt])
return api_fixed
api_blueprint = Blueprint('api', __name__,
url_prefix="{context_path}/api/v1".format(context_path=settings.CONTEXT_PATH))
api = api_patches(api_blueprint)
Hope this help someone not to expend too much time on this like me.
For others who land here via Google or what have you, check this:
in my setup with load balancer and url rewrite www.domain.com/api/
### This fix, will insert <domain>/api/<url for swagger doc path>
@property
def fix_specs_url(self):
return "/api/" + url_for(self.endpoint('specs'), _external=False)
### This fix, will insert <domain>/api/<url for swagger base endpoint path>
@property
def fix_base_url(self):
return "/api" + url_for(self.endpoint('root'), _external=False)
Api.specs_url = fix_specs_url
Api.base_path = fix_base_url
In our code: app = Flask(name) blueprint = Blueprint('api',name,url_prefix='/api') api = Api(blueprint, doc='/doc/') app.register_blueprint(blueprint)
From the log..it's not using the prefix...swaggerui is off root.
10.2.229.197 - - [28/Aug/2018:16:13:36 +0000] "GET /swaggerui/bower/swagger-ui/dist/fonts/DroidSans-Bold.ttf HTTP/1.1" 200 0 "http://10.99.72.221:5002/api/doc/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8"
It's causing issues for AWS Application Load Balancer, as the only route I can define is /api, since it's running inside a Docker container.