Flask RESTful cross-domain issue with Angular: PUT, OPTIONS methods

57,983

Solution 1

I resolved the issue by rewriting my Flask backend to answer with an Access-Control-Allow-Origin header in my PUT response. Furthermore, I created an OPTIONS handler in my Flask app to answer the options method by following what I read in the http RFC.

The return on the PUT method looks like this:

return restful.request.form, 201, {'Access-Control-Allow-Origin': '*'} 

My OPTIONS method handler looks like this:

def options (self):
    return {'Allow' : 'PUT' }, 200, \
    { 'Access-Control-Allow-Origin': '*', \
      'Access-Control-Allow-Methods' : 'PUT,GET' }

@tbicr is right: Flask DOES answer the OPTIONS method automatically for you. However, in my case it wasn't transmitting the Access-Control-Allow-Origin header with that answer, so my browser was getting a reply from the api that seemed to imply that cross-domain requests were not permitted. I overloaded the options request in my case and added the ACAO header, and the browser seemed to be satisfied with that, and followed up OPTIONS with a PUT that also worked.

Solution 2

With the Flask-CORS module, you can do cross-domain requests without changing your code.

from flask.ext.cors import CORS

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

Update

As Eric suggested, the flask.ext.cors module is now deprecated, you should rather use the following code:

from flask_cors import CORS

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

Solution 3

You can use the after_request hook:

@app.after_request
def after_request(response):
    response.headers.add('Access-Control-Allow-Origin', '*')
    response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
    response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
    return response

Solution 4

How about this workaround:

from flask import Flask
from flask.ext import restful
from flask.ext.restful import Api
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config.from_object('config')

#flask-sqlalchemy
db = SQLAlchemy(app)

#flask-restful
api = restful.Api(app)

@app.after_request

def after_request(response):
  response.headers.add('Access-Control-Allow-Origin', '*')
  response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
  response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
  return response

import views

I took this from this tutorial. Works very good. actually, i think this is the best approach I've seen so far.

Returning {'Access-Control-Allow-Origin': '*'} on each endpoint, doesn't seems to be efficient since you have to add it on every endpoint. a bit anoying..., at least for me.

I tried @cors.crossdomain(origin='*') but, looks like it only works with GET request.

Solution 5

You're right, OPTIONS method called every time before real request in browser. OPTIONS response have allowed methods and headers. Flask automatically process OPTIONS requests.

To get access for cross domain request you API must have Access-Control-Allow-Origin header. It can contain specific domains, but if you want allow requests from any domains you can set it to Access-Control-Allow-Origin: *.

To set up CORS for flask you can look at one extension code or try use this extension: https://github.com/wcdolphin/flask-cors/blob/master/flask_cors.py.

To set up CORS for flask-restful look this pull requests: https://github.com/twilio/flask-restful/pull/122 and https://github.com/twilio/flask-restful/pull/131. But looks like flask-restful does not support it by default yet.

Share:
57,983

Related videos on Youtube

rgb
Author by

rgb

Updated on July 09, 2022

Comments

  • rgb
    rgb almost 2 years

    I've developed a small write-only REST api with Flask Restful that accepts PUT request from a handful of clients that can potentially have changing IP addresses. My clients are embedded Chromium clients running an AngularJS front-end; they authenticate with my API with a simple magic key -- it's sufficient for my very limited scale.

    I'm testing deploying my API now and I notice that the Angular clients are attempting to send an OPTIONS http methods to my Flask service. My API meanwhile is replying with a 404 (since I didn't write an OPTIONS handler yet, only a PUT handler). It seems that when sending cross-domain requests that are not POST or GET, Angular will send a pre-flight OPTIONS method at the server to make sure the cross-domain request is accepted before it sends the actual request. Is that right?

    Anyway, how do I allow all cross-domain PUT requests to Flask Restful API? I've used cross-domaion decorators with a (non-restful) Flask instance before, but do I need to write an OPTIONS handler as well into my API?

  • awzx
    awzx almost 7 years
    Thanks ! Works perfectly.
  • silentsudo
    silentsudo over 4 years
    this is partial answer, untill i added @app.after_request stackoverflow.com/questions/23741362/…
  • Mathieu Rodic
    Mathieu Rodic over 4 years
    The code above works as is. The link you provided shows a different solution to the same problem.
  • drysdam
    drysdam almost 3 years
    Golden answer. I get that Flask-CORS is simpler for some people, but on a locked-down system where you have to fill out paperwork to change directories, let alone software, these 7 lines save lives.