FLASK-CORS(1)

πŸ“¦
version 4.0.0 Β· 4 versions
, in ubuntu noble

πŸ¦‹ FLASK-CORS πŸ’– the flask-cors manual page, section 1, iykyk

okay bestie, sit down, hydrate, and let's talk about the most iconic little Flask extension to ever handle a preflight request πŸ‘‘


✨ NAME ✨

flask-cors β€” this is the Flask-Cors documentation, and yes it's giving exactly what it needs to give.

It's a Flask extension for handling Cross Origin Resource Sharing (CORS), which is the whole reason cross-origin AJAX is even possible. no cap. 🫢

The vibe philosophy of this package is so simple, and honestly it ate: when you want to enable CORS, you want to enable it for all use cases on a domain. That means no mucking around with different allowed headers, methods, etc. we're not doing that here, periodt. πŸ’…

By default, submission of cookies across domains is disabled, because... security implications, bestie, they're real 😭. Please see the documentation for how to enable credential'ed requests, and please, please make sure you add some sort of CSRF protection before doing so!

fr fr. cookies across domains with no CSRF protection is not the main character energy you want. it's giving incident report πŸ’€

🌸 INSTALLATION 🌸

Install the extension with pip, or easy_install.

$ pip install -U flask-cors

that's it. that's the whole section. slay ✨


πŸŽ€ USAGE πŸŽ€

This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods. It also allows parameterization of all CORS headers on a per-resource level. And because it's generous like that, the package also contains a decorator, for those who prefer this approach πŸ’–


🌈 Simple Usage

In the simplest case, initialize the Flask-Cors extension with default arguments in order to allow CORS for all domains on all routes. See the full list of options in the documentation.

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)

@app.route("/")
def helloWorld():
  return "Hello, cross-origin-world!"

look at that. one line. CORS(app). effortless, iconic, no notes 🧚


πŸ¦‹ Resource specific CORS

Alternatively, you can specify CORS options on a resource and origin level of granularity by passing a dictionary as the resources option, mapping paths to a set of options. See the full list of options in the documentation.

app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

@app.route("/api/v1/users")
def list_users():
  return "user example"

so you're like "hey, only /api/* gets the wildcard treatment" and Flask-Cors is like "say less" πŸ’…


⭐ Route specific CORS via decorator

This extension also exposes a simple decorator to decorate flask routes with. Simply add @cross_origin() below a call to Flask's @app.route(..) to allow CORS on a given route. See the full list of options in the decorator documentation.

@app.route("/")
@cross_origin()
def helloWorld():
  return "Hello, cross-origin-world!"

one decorator, one route, one very happy browser 🌸


πŸ’œ DOCUMENTATION πŸ’œ

For a full list of options, please see the full documentation.

(yes that's really all it says here. go read the docs, bestie, they're right there.)


πŸ”₯ TROUBLESHOOTING πŸ”₯

If things aren't working as you expect, enable logging to help understand what is going on under the hood, and why. πŸ•΅οΈ

logging.getLogger('flask_cors').level = logging.DEBUG

ngl this one line has saved so many people from a five-hour debugging spiral 😭 turn it on, read the output, thank me later.


πŸ“ TESTS πŸ“

A simple set of tests is included in test/. To run, install nose, and simply invoke nosetests or python setup.py test to exercise the tests.

If nosetests does not work for you, due to it no longer working with newer python versions, you can use pytest to run the tests instead.

lowkey a glow-up arc for the test runner. nose said "i'm retiring", pytest said "bet" πŸ’…

🌈 CONTRIBUTING 🌈

Questions, comments or improvements? Please create an issue on Github, tweet at @corydolphin or send me an email. I do my best to include every contribution proposed in any way that I can. 🫢


πŸ‘‘ CREDITS πŸ‘‘

This Flask extension is based upon the Decorator for the HTTP Access Control written by Armin Ronacher.

This Flask extension is based upon the Decorator for the HTTP Access Control written by Armin Ronacher.

(yes, it says it twice in the original. we keep the lore intact. respect the elders 🧚)


βš™οΈ Configuration

Flask-Cors can be configured at four different locations. Configuration values are determined in the following order:

  1. Resource level settings (e.g when passed as a dictionary)
  2. Keyword argument settings
  3. App level configuration settings (e.g. CORS_*)
  4. Default settings

See below for more information.


🧾 Configuration options

Configuration options are consistently named across the various locations where they can be set. A configuration option called example can be set with the resource dictionary key example, as the keyword argument example or as the Flask app configuration key CORS_EXAMPLE.

The configuration options recognised by Flask-Cors are:

  • CORS_ALLOW_HEADERS (List or str) β€” Headers to accept from the client. Headers in the Access-Control-Request-Headers request header (usually part of the preflight OPTIONS request) matching headers in this list will be included in the Access-Control-Allow-Headers response header.
  • CORS_ALWAYS_SEND (bool) β€” Usually, if a request doesn't include an Origin header, the client did not request CORS. This means we can ignore this request. However, if this is true, a most-likely-to-be-correct value is still set.
  • CORS_AUTOMATIC_OPTIONS (bool) β€” Only applies to the flask_cors.cross_origin() decorator. If True, Flask-Cors will override Flask's default OPTIONS handling to return CORS headers for OPTIONS requests.
  • CORS_EXPOSE_HEADERS (List or str) β€” The CORS spec requires the server to give explicit permissions for the client to read headers in CORS responses (via the Access-Control-Expose-Headers header). This specifies the headers to include in this header.
  • CORS_INTERCEPT_EXCEPTIONS (bool) β€” Whether to deal with Flask exception handlers or leave them alone (with respect to CORS headers).
  • CORS_MAX_AGE (timedelta, int or str) β€” The maximum time for which this CORS request may be cached. This value is set as the Access-Control-Max-Age header.
  • CORS_METHODS (List or str) β€” The method(s) which the allowed origins are allowed to access. These are included in the Access-Control-Allow-Methods response headers to the preflight OPTIONS requests.
  • CORS_ORIGINS (List, str or re.Pattern) β€” The origin(s) to allow requests from. An origin configured here that matches the value of the Origin header in a preflight OPTIONS request is returned as the value of the Access-Control-Allow-Origin response header.
  • CORS_RESOURCES (Dict, List or str) β€” The series of regular expression and (optionally) associated CORS options to be applied to the given resource path.
    • If the value is a dictionary, it's keys must be regular expressions matching resources, and the values must be another dictionary of configuration options, as described in this section.
    • If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied.
    • If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied.
  • CORS_SEND_WILDCARD (bool) β€” If CORS_ORIGINS is "*" and this is true, then the Access-Control-Allow-Origin response header's value with be "*" as well, instead of the value of the Origin request header.
  • CORS_SUPPORTS_CREDENTIALS (bool) β€” Allows users to make authenticated requests. If true, injects the Access-Control-Allow-Credentials header in responses. This allows cookies and credentials to be submitted across domains.
note β€” This option cannot be used in conjunction with a "*" origin.
  • CORS_VARY_HEADER: (bool) β€” Enables or disables the injection of the Vary response header is set to Origin. This informs clients that our CORS headers are dynamic and cannot be cached.

🌟 Default values

  • CORS_ALLOW_HEADERS: "*"
  • CORS_ALWAYS_SEND: True
  • CORS_AUTOMATIC_OPTIONS: True
  • CORS_EXPOSE_HEADERS: None
  • CORS_INTERCEPT_EXCEPTIONS: True
  • CORS_MAX_AGE: None
  • CORS_METHODS: ["GET", "HEAD", "POST", "OPTIONS", "PUT", "PATCH", "DELETE"]
  • CORS_ORIGINS: "*"
  • CORS_RESOURCES: r"/*"
  • CORS_SEND_WILDCARD: False
  • CORS_SUPPORTS_CREDENTIALS: False
  • CORS_VARY_HEADER: True

πŸ“ Locations

🧩 Resource level settings

You can specify CORS options on a resource level of granularity by passing a dictionary as the resources keyword argument when instantiating the flask_cors.CORS object (or when calling init_app on it), mapping paths to a set of options.

πŸ—£οΈ Keyword argument settings

For options matching all resources, it's also possible to simply set the configuration options using keyword arguments when instantiating the flask_cors.CORS object (or when calling init_app on it).

🏠 App level configuration settings

It's good practice to keep your application configuration settings in one place. This is also possible with Flask-Cors using the same configuration options in the Flask application's config object.

🎈 Default settings

Finally, every setting has a default value as well.


πŸ“š API Docs πŸ“š

This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods. It allows parameterization of all CORS headers on a per-resource level. The package also contains a decorator, for those who prefer this approach.


🧬 Extension

This is the suggested approach to enabling CORS. The default configuration will work well for most use cases. πŸ’–

class flask_cors.CORS(app=None, **kwargs)

Initializes Cross Origin Resource sharing for the application. The arguments are identical to cross_origin(), with the addition of a resources parameter. The resources parameter defines a series of regular expressions for resource paths to match and optionally, the associated options to be applied to the particular resource. These options are identical to the arguments to cross_origin().

The settings for CORS are determined in the following order:

  1. Resource level settings (e.g when passed as a dictionary)
  2. Keyword argument settings
  3. App level configuration settings (e.g. CORS_*)
  4. Default settings

Note: as it is possible for multiple regular expressions to match a resource path, the regular expressions are first sorted by length, from longest to shortest, in order to attempt to match the most specific regular expression. This allows the definition of a number of specific resource options, with a wildcard fallback for all other resources. 🎯

Parameters

  • resources (dict, iterable or string) β€”

The series of regular expression and (optionally) associated CORS options to be applied to the given resource path.

If the argument is a dictionary, it's keys must be regular expressions, and the values must be a dictionary of kwargs, identical to the kwargs of this function.

If the argument is a list, it's expected to be a list of regular expressions, and the app-wide configured options get applied to them. If the argument is a string, it's expected to be a regular expression, and again the app-wide configured options get applied to it.

Default : Match all and apply app-level configuration πŸ’«

  • origins (list, string or regex) β€” the origin, or the list of origins, you're allowing requests from. The origin(s) can be regular expressions, case-sensitive strings, or else an asterisk. flexible much?
note β€” origins must include the schema and the port (unless it's port 80), bestie. no skipping the schema, that's the rules 😀

for example, CORS(app, origins=["http://localhost:8000", "https://example.com"]).

Default : '*'

  • methods (list or string) β€” the method, or the list of methods, that the allowed origins are allowed to access for non-simple requests.

Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]

  • expose_headers (list or string) β€” the header, or the list, that's safe to expose to the API of a CORS API specification.

Default : None

  • allow_headers (list, string or regex) β€” the header, or the list of header field names, that can be used when this resource is accessed by allowed origins. The header(s) can be regular expressions, case-sensitive strings, or else an asterisk.

Default : '*', allow all headers πŸ‘‘

  • supports_credentials (bool) β€” lets users make authenticated requests. If true, it injects the Access-Control-Allow-Credentials header into responses. This is what allows cookies and credentials to be submitted across domains.
note β€” this option cannot be used in conjunction with a '*' origin. pick a lane, bestie πŸ’€

Default : False

  • max_age (timedelta, integer, string or None) β€” the maximum time for which this CORS request maybe cached. This value is set as the Access-Control-Max-Age header.

Default : None

  • send_wildcard (bool) β€” if True, and the origins parameter is *, a wildcard Access-Control-Allow-Origin header gets sent, rather than the request's Origin header.

Default : False

  • vary_header (bool) β€” if True, the header Vary: Origin will be returned as per the W3 implementation guidelines.

Setting this header when the Access-Control-Allow-Origin is dynamically generated (like when there's more than one allowed origin, and an Origin than '*' is returned) informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached. periodt.

If False, the Vary header will never be injected or altered. like, ever. 🚫

Default : True


πŸŽ€ Decorator πŸŽ€

If the CORS extension isn't giving what you need, the decorator might be your girl. She shares options with the extension, and she's simple to use. iykyk

flask_cors.cross_origin(*args, **kwargs)

This function is the decorator that's used to wrap a Flask route with. In the simplest case, just use the default parameters to allow all origins in what is the most permissive configuration. ⚠️ important though: if this method modifies state or performs authentication which may be brute-forced, you should add some degree of protection, such as Cross Site Request Forgery protection. do not skip this step bestie, i'm literally begging πŸ™

Parameters

  • origins (list, string or regex) β€” the origin, or list of origins, to allow requests from. The origin(s) may be regular expressions, case-sensitive strings, or else an asterisk.

Default : '*'

  • methods (list or string) β€” the method or list of methods which the allowed origins are allowed to access for non-simple requests.

Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE]

  • expose_headers (list or string) β€” the header or list which are safe to expose to the API of a CORS API specification.

Default : None

  • allow_headers (list, string or regex) β€” the header or list of header field names which can be used when this resource is accessed by allowed origins. The header(s) may be regular expressions, case-sensitive strings, or else an asterisk.

Default : '*', allow all headers

  • supports_credentials (bool) β€” allows users to make authenticated requests. If true, injects the Access-Control-Allow-Credentials header in responses. This allows cookies and credentials to be submitted across domains.
note β€” this option cannot be used in conjunction with a '*' origin 🚩

Default : False

  • max_age (timedelta, integer, string or None) β€” the maximum time for which this CORS request maybe cached. This value is set as the Access-Control-Max-Age header.

Default : None

  • send_wildcard (bool) β€” if True, and the origins parameter is *, a wildcard Access-Control-Allow-Origin header is sent, rather than the request's Origin header.

Default : False

  • vary_header (bool) β€” if True, the header Vary: Origin will be returned as per the W3 implementation guidelines.

Setting this header when the Access-Control-Allow-Origin is dynamically generated (e.g. when there is more than one allowed origin, and an Origin than '*' is returned) informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached.

If False, the Vary header will never be injected or altered.

Default : True

  • automatic_options (bool) β€” only applies to the cross_origin decorator. If True, Flask-CORS will override Flask's default OPTIONS handling to return CORS headers for OPTIONS requests.

Default : True


πŸͺ Using CORS with cookies πŸͺ

By default, Flask-CORS does not allow cookies to be submitted across sites, since it has potential security implications. If you wish to enable cross-site cookies, you may wish to add some sort of CSRF protection to keep you and your users safe. protect the girlies!!

To allow cookies or authenticated requests to be made cross origins, simply set the supports_credentials option to True. E.g.

from flask import Flask, session
from flask_cors import CORS

app = Flask(__name__)
CORS(app, supports_credentials=True)

@app.route("/")
def helloWorld():
  return "Hello, %s" % session['username']

The above code enables Flask backend to accept cookies to be submitted from cross origin sites. But if you are sending Xhr requests (ajax calls) to a cross-origin server, by default chrome or any modern browser won't send cookies and session with the request. You should use withCredentials = True while sending Xhr request to enable that. You should keep in mind about the necessary security concerns. Related MDN doc: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials


πŸ¦‹ Using CORS with Blueprints πŸ¦‹

Flask-CORS supports blueprints out of the box. Simply pass a blueprint instance to the CORS extension, and everything will just work. no notes, it's giving effortless ✨

api_v1 = Blueprint('API_v1', __name__)

CORS(api_v1) # enable CORS on the API_v1 blue print

@api_v1.route("/api/v1/users/")
def list_users():
    '''
        Since the path matches the regular expression r'/api/*', this resource
        automatically has CORS headers set. The expected result is as follows:

        $ curl --include -X GET http://127.0.0.1:5000/api/v1/users/ \
            --header Origin:www.examplesite.com
        HTTP/1.0 200 OK
        Access-Control-Allow-Headers: Content-Type
        Access-Control-Allow-Origin: *
        Content-Length: 21
        Content-Type: application/json
        Date: Sat, 09 Aug 2014 00:26:41 GMT
        Server: Werkzeug/0.9.4 Python/2.7.8

        {
            "success": true
        }

    '''
    return jsonify(user="joe")

@api_v1.route("/api/v1/users/create", methods=['POST'])
def create_user():
    '''
        Since the path matches the regular expression r'/api/*', this resource
        automatically has CORS headers set.

        Browsers will first make a preflight request to verify that the resource
        allows cross-origin POSTs with a JSON Content-Type, which can be simulated
        as:
        $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \
            --header Access-Control-Request-Method:POST \
            --header Access-Control-Request-Headers:Content-Type \
            --header Origin:www.examplesite.com
        >> HTTP/1.0 200 OK
        Content-Type: text/html; charset=utf-8
        Allow: POST, OPTIONS
        Access-Control-Allow-Origin: *
        Access-Control-Allow-Headers: Content-Type
        Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
        Content-Length: 0
        Server: Werkzeug/0.9.6 Python/2.7.9
        Date: Sat, 31 Jan 2015 22:25:22 GMT

        $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \
            --header Content-Type:application/json \
            --header Origin:www.examplesite.com

    '''
    return jsonify(user="joe")

Wait β€” hold on, the original code block ends right there mid-comment, so I'm keeping it exactly as it is. the suspense is real 😭

                  >> HTTP/1.0 200 OK
                  Content-Type: application/json
                  Content-Length: 21
                  Access-Control-Allow-Origin: *
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:04 GMT

                  {
                    "success": true
                  }

              '''
              return jsonify(success=True)

          public_routes = Blueprint('public', __name__)

          @public_routes.route("/")
          def helloWorld():
              '''
                  Since the path '/' does not match the regular expression r'/api/*',
                  this route does not have CORS headers set.
              '''
              return '''<h1>Hello CORS!</h1> Read about my spec at the
          <a href="http://www.w3.org/TR/cors/">W3</a> Or, checkout my documentation
          on <a href="https://github.com/corydolphin/flask-cors">Github</a>'''

          logging.basicConfig(level=logging.INFO)
          app = Flask('FlaskCorsBlueprintBasedExample')
          app.register_blueprint(api_v1)
          app.register_blueprint(public_routes)

          if __name__ == "__main__":
              app.run(debug=True)

so real talk about what you just scrolled past πŸ’… β€” the api_v1 blueprint is the one matching r'/api/*', so it gets the CORS headers, and public_routes is the blueprint whose / route does not match, which the docstring is literally spelling out for you. Then logging.basicConfig(level=logging.INFO) sets up logging, the app gets named 'FlaskCorsBlueprintBasedExample', both blueprints get registered with app.register_blueprint(...), and it all kicks off with app.run(debug=True). the blueprint-based setup ate and left no crumbs 🫢


✨ Examples ✨

okay bestie this is the part you actually came for 😭 two whole worked examples, end to end, nothing skipped, not even the curl output. grab a snack. πŸ“


✨ Using the CORS extension ✨

first up: the extension route. one CORS(app, resources=r'/api/*') line and everything under /api/* is speaking fluent CORS. let's go 🧚

          # One of the simplest configurations. Exposes all resources matching /api/* to
          # CORS and allows the Content-Type header, which is necessary to POST JSON
          # cross origin.
          CORS(app, resources=r'/api/*')

          @app.route("/")
          def helloWorld():
              """
                  Since the path '/' does not match the regular expression r'/api/*',
                  this route does not have CORS headers set.
              """
              return '''
          <html>
              <h1>Hello CORS!</h1>
              <h3> End to end editable example with jquery! </h3>
              <a class="jsbin-embed" href="http://jsbin.com/zazitas/embed?js,console">JS Bin on jsbin.com</a>
              <script src="//static.jsbin.com/js/embed.min.js?3.35.12"></script>

          </html>
          '''

notice how / is out here living its no-CORS-headers life because it doesn't match the regex? that's not a bug, that's the whole point. iconic behavior honestly.

still the same file, just breaking it up so your eyes don't fall out πŸ’€ β€” here's the route that does match, plus the expected curl result:

          @app.route("/api/v1/users/")
          def list_users():
              """
                  Since the path matches the regular expression r'/api/*', this resource
                  automatically has CORS headers set. The expected result is as follows:

                  $ curl --include -X GET http://127.0.0.1:5000/api/v1/users/ \
                      --header Origin:www.examplesite.com
                  HTTP/1.0 200 OK
                  Access-Control-Allow-Headers: Content-Type
                  Access-Control-Allow-Origin: *
                  Content-Length: 21
                  Content-Type: application/json
                  Date: Sat, 09 Aug 2014 00:26:41 GMT
                  Server: Werkzeug/0.9.4 Python/2.7.8

                  {
                      "success": true
                  }

              """
              return jsonify(user="joe")

and now the POST route, where the browser does its lil preflight first. both the OPTIONS and the actual POST are simulated below, verbatim, because we do not gatekeep πŸ™ˆ

          @app.route("/api/v1/users/create", methods=['POST'])
          def create_user():
              """
                  Since the path matches the regular expression r'/api/*', this resource
                  automatically has CORS headers set.

                  Browsers will first make a preflight request to verify that the resource
                  allows cross-origin POSTs with a JSON Content-Type, which can be simulated
                  as:
                  $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \
                      --header Access-Control-Request-Method:POST \
                      --header Access-Control-Request-Headers:Content-Type \
                      --header Origin:www.examplesite.com
                  >> HTTP/1.0 200 OK
                  Content-Type: text/html; charset=utf-8
                  Allow: POST, OPTIONS
                  Access-Control-Allow-Origin: *
                  Access-Control-Allow-Headers: Content-Type
                  Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
                  Content-Length: 0
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:22 GMT

                  $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \
                      --header Content-Type:application/json \
                      --header Origin:www.examplesite.com

                  >> HTTP/1.0 200 OK
                  Content-Type: application/json
                  Content-Length: 21
                  Access-Control-Allow-Origin: *
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:04 GMT

                  {
                    "success": true
                  }

              """
              return jsonify(success=True)

last chunk of this example: the route that just throws so you can see how the error handler behaves, plus the 500 handler itself and the app.run at the bottom. we close this file out properly ✨

          @app.route("/api/exception")
          def get_exception():
              """
                  Since the path matches the regular expression r'/api/*', this resource
                  automatically has CORS headers set.

                  Browsers will first make a preflight request to verify that the resource
                  allows cross-origin POSTs with a JSON Content-Type, which can be simulated
                  as:
                  $ curl --include -X OPTIONS http://127.0.0.1:5000/api/exception \
                      --header Access-Control-Request-Method:POST \
                      --header Access-Control-Request-Headers:Content-Type \
                      --header Origin:www.examplesite.com
                  >> HTTP/1.0 200 OK
                  Content-Type: text/html; charset=utf-8
                  Allow: POST, OPTIONS
                  Access-Control-Allow-Origin: *
                  Access-Control-Allow-Headers: Content-Type
                  Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
                  Content-Length: 0
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:22 GMT
              """
              raise Exception("example")

          @app.errorhandler(500)
          def server_error(e):
              logging.exception('An error occurred during a request. %s', e)
              return "An internal error occurred", 500

          if __name__ == "__main__":
              app.run(debug=True)

see that raise Exception("example")? bold move. and the 500 handler catches it, logs it with logging.exception(...), and hands back "An internal error occurred", 500. periodt. πŸ’…


✨ Using the cross_origin decorator ✨

now the decorator route β€” instead of one big CORS(app, ...) call, you sprinkle @cross_origin() on the individual views. main character energy, per-route edition πŸ‘‘

first view: bare @cross_origin(), which is the simplest possible view-based decoration. CORS on for all domains 🌈

          @app.route("/", methods=['GET'])
          @cross_origin()
          def helloWorld():
              '''
                  This view has CORS enabled for all domains, representing the simplest
                  configuration of view-based decoration. The expected result is as
                  follows:

                  $ curl --include -X GET http://127.0.0.1:5000/ \
                      --header Origin:www.examplesite.com

                  >> HTTP/1.0 200 OK
                  Content-Type: text/html; charset=utf-8
                  Content-Length: 184
                  Access-Control-Allow-Origin: *
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:29:56 GMT

                  <h1>Hello CORS!</h1> Read about my spec at the
                  <a href="http://www.w3.org/TR/cors/">W3</a> Or, checkout my documentation
                  on <a href="https://github.com/corydolphin/flask-cors">Github</a>

              '''
              return '''<h1>Hello CORS!</h1> Read about my spec at the
          <a href="http://www.w3.org/TR/cors/">W3</a> Or, checkout my documentation
          on <a href="https://github.com/corydolphin/flask-cors">Github</a>'''

second view: same deal, but this time we pass allow_headers=['Content-Type'] so browsers can send that header and cross-domain AJAX POSTs can actually happen. two curl calls again β€” preflight OPTIONS, then the real POST πŸ¦‹

          @app.route("/api/v1/users/create", methods=['GET', 'POST'])
          @cross_origin(allow_headers=['Content-Type'])
          def cross_origin_json_post():
              '''
                  This view has CORS enabled for all domains, and allows browsers
                  to send the Content-Type header, allowing cross domain AJAX POST
                  requests.

           Browsers will first make a preflight request to verify that the resource
                  allows cross-origin POSTs with a JSON Content-Type, which can be simulated
                  as:
                  $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \
                      --header Access-Control-Request-Method:POST \
                      --header Access-Control-Request-Headers:Content-Type \
                      --header Origin:www.examplesite.com
                  >> HTTP/1.0 200 OK
                  Content-Type: text/html; charset=utf-8
                  Allow: POST, OPTIONS
                  Access-Control-Allow-Origin: *
                  Access-Control-Allow-Headers: Content-Type
                  Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT
                  Content-Length: 0
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:22 GMT

                  $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \
                      --header Content-Type:application/json \
                      --header Origin:www.examplesite.com

                  >> HTTP/1.0 200 OK
                  Content-Type: application/json
                  Content-Length: 21
                  Access-Control-Allow-Origin: *
                  Server: Werkzeug/0.9.6 Python/2.7.9
                  Date: Sat, 31 Jan 2015 22:25:04 GMT

                  {
                    "success": true
                  }

              '''

              return jsonify(success=True)

          if __name__ == "__main__":
              app.run(debug=True)

and that's the whole tour bestie, decorator style, from @app.route all the way down to app.run(debug=True). no cap. πŸ”₯


πŸ’– AUTHOR πŸ’–

  • Cory Dolphin β€” the icon, the legend, the whole reason this page exists πŸ‘‘

  • 2023, Cory Dolphin 🌸

and that's a wrap on FLASK-CORS(1) fr fr πŸŽ€ this was version 4.0.0, dated Jun 27, 2023 β€” the one and only FLASK-CORS(1) manual page πŸ’œ go forth and cross-origin responsibly, bestie πŸ¦‹βœ¨

πŸ“¦ where this page comes from