12

I am relatively new to python and I created a micro service using flask-resplus. Works fine on my computer and on the dev server served with http. I dont have control on where the microservice could be deployed. In these case it seems is behind a load balancer(not sure of details), served with https.

The actual errors given by the browser: Can't read from server. It may not have the appropriate access-control-origin settings.

When i check the network developer tools i see it fails loading swagger.json. But is checking it using: http://hostname/api/swagger.json, instead of https.

I have been googling, and i ran into discussions of this issue. And this seemed to be the fix that could work without me having to change the library or configurations on the server.

However still i couldnt get it to work.

This is what i have:

on the api file:

api_blueprint = Blueprint('api', __name__, url_prefix='/api')
api = Api(api_blueprint, doc='/doc/', version='1.0', title='My api',
          description="My api")

on the main app file:

from flask import Flask
from werkzeug.contrib.fixers import ProxyFix

from lib.api import api_blueprint

app = Flask(__name__)

app.wsgi_app = ProxyFix(app.wsgi_app)
app.register_blueprint(api_blueprint)

Also tried adding:

app.config['SERVER_URL'] = 'http://testfsdf.co.za' # but it dont look like is being considered

Using flask-restplus==0.9.2,

Any solution will be appreciated, as long as i dont need to make configurations on the container where the service will be deployed (am ok with setting environment variables), i.e. service need to be self contained. And if there is a version of flask-resplus that i can install with pip, that already has a fix i can appreciate.

Thanks a lot guys,

Mosd
  • 1,420
  • 18
  • 21

2 Answers2

10

Overide API class with _scheme='https' option in spec property.

class MyApi(Api):
    @property
    def specs_url(self):
        """Monkey patch for HTTPS"""
        scheme = 'http' if '5000' in self.base_url else 'https'
        return url_for(self.endpoint('specs'), _external=True, _scheme=scheme)

api = MyApi(api_blueprint, doc='/doc/', version='1.0', title='My api',
        description="My api")
Ilya Davydov
  • 461
  • 7
  • 11
  • Thanks, this worked for me when running my application in production. But when I run my app locally, I get the following response "Can't read from server. It may not have the appropriate access-control-origin settings" from calling localhost:5000. Is there anyway I can get this to run the same in both prod and locally? – Yogi Valani Oct 16 '18 at 15:27
  • I got your problem. Just use default scheme in local enviroment, see edited code – Ilya Davydov Oct 17 '18 at 10:33
1

The solution above works like a charm. There are couple of things you should check.

  1. Before applying the fix, make sure in your chrome developertools -> Network tab that whenever you reload the page(in https server) that shows the swagger UI, you get a mixed content error for swagger.json request.

  2. The solution in the above post solves the issue when deployed on an https server but locally it might give issue. For that you can use the environment variable trick.

  3. Set a custom environment variable or any variable which is already there on your https server while deploying your app. Check for the existence of that environment variable before applying the solution to make sure your app in running in the https server.

Now when you run the app locally, this hack won't be applied and swagger.json would be served through http and in your server it would be served via https. Implementation might look similar to this.

import os
from flask import url_for
from flask_restplus import Api

app = Flask( __name__)
if os.environ.get('CUSTOM_ENV_VAR'):
    @property
    def specs_url(self):
        return url_for(self.endpoint('specs'), _external=True, _scheme='https')
    Api.specs_url = specs_url
api = Api(app)
Binoy S Kumar
  • 201
  • 3
  • 8