How to solve “No API definition provided” error for Flask-RESTPlus app on Cloud Foundry
When you create an API endpoint that generates a QRCode with Flask-RESTPlus, you may want to deploy it onto Cloud Foundry.
However, when I first tried to do so, I was greeted with a "No API definition provided" when I accessed the Swagger Api portal page.
So why is there a "No API definition provided" error for Flask-RESTPlus app on Cloud Foundry?
In case you are facing such a problem, this post discuss how I had managed to solve the "No API definition provided" error for my Flask-RESTPlus app on Cloud Foundry.
Having an idea of how the Flask-RESTPlus app was deployed on Cloud Foundry
Before diving into the solution, it is helpful to know how the Flask-RESTPlus app was deployed onto Cloud Foundry.
In addition to the python file, the QRCode app was pushed to a Cloud Foundry environment with the following manifest file:
--- applications: - name: QRCodeApp memory: 256MB disk_quota: 256MB random-route: true buildpack: python_buildpack command: python run_app.py instances: 1
Given that, Cloud Foundry will make the Flask-RESTPlus app accessible via https://qrcodeapp.somcfurl.com.
Whenever HTTP request is made to that endpoint, the reverse proxy server will proxy HTTP requests to the web application that was started with the following command:
python run_app.py
Finding out why there is a "No API definition provided" error with Chrome Inspector
Whenever there is a problem with a web page that you are accessing, the Chrome Inspector is a tool that can tell you more about the problem.
Given that, I started the Chrome Inspector and found the following error message:
Mixed Content: The page at 'https://qrcodeapp.somcfurl.com/api/' was loaded over HTTPS, but requested an insecure resource 'http://qrcodeapp.somcfurl.com/api/swagger.json'. This request has been blocked; the content must be served over HTTPS.
(anonymous) @ index.js:1
So what does this error message mean?
Since Cloud Foundry had served our portal page over HTTPS, our Chrome browser expects that subsequent requests should be made via HTTPS.
However, our command had started the Flask-RESTPlus app serving HTTP requests over HTTP.
Therefore, there was a call made to retrieve a resource (http://qrcodeapp.somcfurl.com/api/swagger.json) via HTTP in the HTML document that was returned as a HTTP response to the browser.
Since that call was block, the bootstrapping JavaScript code from Swagger was unable to replace the "No API definition provided" text with the elements of the API portal.
Instructing the Flask-RESTPlus Api object to generate urls with the HTTPS scheme instead of HTTP
Since there is a mismatch of scheme used, we have to configure the Flask-RESTPlus Api object to generate urls with HTTPS instead of HTTP.
In addition to that, we want to be able to test our Flask-RESTPlus app in our development machine when we access the API portal via HTTP.
Given that, we need a way for our Flask-RESTPlus app to know when to generate urls with HTTPS.
As I had mentioned previously, we can get the environment variables supplied to our Cloud Foundry application with Python 3 Flask. Given that, we can get a list of environment variables that are supplied to our Flask-RESTPlus app on Cloud Foundry. We can then check the existence of one of the environment variables to determine whether our app is on Cloud Foundry.
For example, the following code checks for the VCAP_SERVICES environment variable before instructing the Flask-RESTPlus Api object to generate urls with HTTPS instead of HTTP:
from flask import url_for from flask_restplus import Api if os.environ.get('VCAP_SERVICES'): @property def specs_url(self): return url_for(self.endpoint('specs'), _external=True, _scheme='https') Api.specs_url = specs_url
Putting the above code fragment with our QRCode app, we yield the following code:
from flask import Flask, Blueprint, request, send_file, url_for from flask_restplus import Api, Namespace, Resource, fields from io import BytesIO import os, qrcode # Create Flask app app = Flask(__name__) # Associate Api with Blueprint api_blueprint = Blueprint('API', __name__) if os.environ.get('VCAP_SERVICES'): @property def specs_url(self): return url_for(self.endpoint('specs'), _external=True, _scheme='https') Api.specs_url = specs_url api = Api(api_blueprint, title='Api for QR code app', version='1.0', description='This is an API for QR code app', # All API metadatas ) # Create namespace for containing Qr Code related operations qrcode_namespace = Namespace('QrCode', description='Qr code related operations') # Specify uri of qrcode namespace as /qrcode api.add_namespace(qrcode_namespace, path='/qrcode') # Specify uri of api blueprint as /api app.register_blueprint(api_blueprint, url_prefix='/api') # Define input model qrcode_creation_input = qrcode_namespace.model('QRCode creation Input', { 'value': fields.String(required=True, description='The value that is supposed to be encoded into qrcode'), }) # Define API endpoint for creating Qr Code image @qrcode_namespace.route('/') @qrcode_namespace.doc('Creates a QRCode image based on a string value.') class QrCodeRoot(Resource): @qrcode_namespace.expect(qrcode_creation_input) @qrcode_namespace.produces(['image/png']) @qrcode_namespace.response(200, description='Return QR Code png image file.') @qrcode_namespace.response(400, description='Invalid input provided.') def post(self): # Get value to encode into QR Code json_input = request.get_json() value_to_turn_into_qrcode = json_input['value'] # Create qr code image and return it as HTTP response pil_img = qrcode.make(value_to_turn_into_qrcode) img_io = BytesIO() pil_img.save(img_io, 'PNG') img_io.seek(0) return send_file(img_io, mimetype='image/png') if __name__ == '__main__': port = int(os.getenv("PORT", "5678")) app.run(host='0.0.0.0', port=port)
Once I had deployed the code changes to Cloud Foundry, I am able to access the API portal without the "No API definition provided" error.