Connexion: [Doubt] Enable CORS with connexion

Created on 2 Dec 2016  ·  9Comments  ·  Source: zalando/connexion

Description

I have a static file with some jQuery that access an API built with connexion. Unfortunatelly, the browser requires CORS to be enable in order to access the API. I couldn't find a way of adding fields to the headers response nor a module or option to do that, so I'm not sure on what to do about.

Additional info:

Another possible workaround would be if I could just serve this static code with Connexion, I found lots of solutions for Flask but nothing that worked with Connexion

Output of the commands:

  • python --version
    Python 3.4.3
question

Most helpful comment

Hi @ernanirst - I believe you can access the underlying flask 'app' object and enable CORS the same way you'd do it with a flask service like so:

import connexion
from connexion.resolver import RestyResolver
from flask_cors import CORS

app = connexion.App(__name__)
CORS(app.app)
app.add_api('api.yaml', resolver=RestyResolver('seaiceservices.api'))

All 9 comments

Hi @ernanirst - I believe you can access the underlying flask 'app' object and enable CORS the same way you'd do it with a flask service like so:

import connexion
from connexion.resolver import RestyResolver
from flask_cors import CORS

app = connexion.App(__name__)
CORS(app.app)
app.add_api('api.yaml', resolver=RestyResolver('seaiceservices.api'))

@ernanirst see #50 for answers/discussion, using flask-cors looks good enough for me.

Re-opening, as we should add a section regarding CORS on http://connexion.readthedocs.io/en/latest/cookbook.html

See my PR #358.

Thanks a lot, I've tried something very similar which did not work, but this worked fine. I suggest that you add this to the docs because I looked there and found nothing. Thanks again.

I'm trying to use flask cors with connexion

#!/usr/bin/env python3

import config
import connexion
import logging
import logging.handlers
from connexion.resolver import RestyResolver
from flask.logging import default_handler
from flask_jwt_extended import JWTManager
from flask_cors import CORS

def create_app():
    app = connexion.FlaskApp(__name__, specification_dir='swagger/')
    app.add_api('mapmybook.yaml', validate_responses=True, strict_validation=True, resolver=RestyResolver('api'))

    app.app.config.from_object(config.DevelopmentConfig)
    app.app.logger.addHandler(create_file_handler())

    jwt = JWTManager(app.app)
    return app.app # return <Flask> object

def create_file_handler():
    ...
    return rfh

if __name__ == '__main__':
    app = create_app()
    CORS(app.app)
    app.run(port=5000)

Yet when I send a request

curl 'http://127.0.0.1:5000/v1.0/login' -X OPTIONS -H 'Pragma: no-cache' -H 'Access-Control-Request-Method: POST' -H 'Origin: http://localhost:8080' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: en-US,en;q=0.9' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36 OPR/56.0.3051.36' -H 'Accept: */*' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Access-Control-Request-Headers: content-type' --compressed

I get
Failed to load http://127.0.0.1:5000/v1.0/login: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access

What else do I have to set to make this work with application/json content type?

Just a stab in the dark. But add the resources argument, for example:

...
CORS_RESOURCES = {r"/v1/*": {"origins": "*"}, r"/v1.0/*": {"origins": "*"}, r"/v1.1/*": {"origins": "*"}}
CORS(app.app, resources=CORS_RESOURCES)
...

Seems @cross_origin decorator solves the problem, but CORS(app) alone is not working.

Just for reference, for simple use cases you can simply add the required headers yourself to all responses. There is two ways to achieve this.

Using a custom middleware:

class CorsHeaderMiddleware(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        def custom_start_response(status, headers, exc_info=None):
            # append whatever headers you need here
            headers.append(('Access-Control-Allow-Origin', '*'))
            headers.append(
                ('Access-Control-Allow-Headers', 'X-Requested-With')
            )
            headers.append(('Access-Control-Allow-Methods', 'OPTIONS'))
            return start_response(status, headers, exc_info)

        return self.app(environ, custom_start_response)

connexion_app = connexion.FlaskApp(__name__, specification_dir='.')
connexion_app.add_api('specification.yml')
connexion_app.app.wsgi_app = CorsHeaderMiddleware(connexion_app.app.wsgi_app)

Using Flask's after_request hook:

def set_cors_headers_on_response(response):
    response.headers['Access-Control-Allow-Origin'] = '*'
    response.headers['Access-Control-Allow-Headers'] = 'X-Requested-With'
    response.headers['Access-Control-Allow-Methods'] = 'OPTIONS'
    return response

connexion_app = connexion.FlaskApp(__name__, specification_dir='.')
connexion_app.add_api('api_specification.yml')
connexion_app.app.after_request(set_cors_headers_on_response)

While the Flask hook approach is a bit shorter, the WSGI middleware will keep working even when switching to a different backend like tornado or gevent later.

Was this page helpful?
0 / 5 - 0 ratings