Reputation: 13908
This is my code:
@app.route('/hello', methods=["POST"])
def hello():
resp = make_response(render_template('hello.html'))
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
However, when I make a request from the browser to my server I get this error:
XMLHttpRequest cannot load http://localhost:5000/hello.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have also tried this approach, setting the response headers "after" the request:
@app.after_request
def add_header(response):
response.headers['Access-Control-Allow-Origin'] = '*'
return response
No dice. I get the same error. Is there a way to just set the response headers in the route function? Something like this would be ideal:
@app.route('/hello', methods=["POST"])
def hello(response): # is this a thing??
response.headers['Access-Control-Allow-Origin'] = '*'
return response
but I cant find anyway to do this. Please help.
EDIT
if I curl the url with a POST request like so:
curl -iX POST http://localhost:5000/hello
I get this response:
HTTP/1.0 500 INTERNAL SERVER ERROR
Content-Type: text/html
Content-Length: 291
Server: Werkzeug/0.9.6 Python/2.7.6
Date: Tue, 16 Sep 2014 03:58:42 GMT
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>500 Internal Server Error</title>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.</p>
Any ideas?
Upvotes: 164
Views: 288299
Reputation: 465
According to the documentation, you can return headers from your view function together with the response.
If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form
(response, status)
,(response, headers)
, or(response, status, headers)
. The status value will override the status code and headers can be a list or dictionary of additional header values.
For example:
@app.route('/hello', methods=["POST"])
def hello():
return flask.make_response(), {"Access-Control-Allow-Origin": "*"}
Or:
@app.route('/hello', methods=["POST"])
def hello():
return {"foo": "bar"}, 200, {"Access-Control-Allow-Origin": "*"}
Upvotes: 23
Reputation: 2039
We can set the response headers in Python Flask application using Flask application context using flask.g
This way of setting response headers in Flask application context using flask.g
is thread safe and can be used to set custom & dynamic attributes from any file of application, this is especially helpful if we are setting custom/dynamic response headers from any helper class, that can also be accessed from any other file ( say like middleware, etc), this flask.g
is global & valid for that request thread only.
Say if i want to read the response header from another api/http call that is being called from this app, and then extract any & set it as response headers for this app.
Sample Code: file: helper.py
import flask
from flask import request, g
from multidict import CIMultiDict
from asyncio import TimeoutError as HttpTimeout
from aiohttp import ClientSession
def _extract_response_header(response)
"""
extracts response headers from response object
and stores that required response header in flask.g app context
"""
headers = CIMultiDict(response.headers)
if 'my_response_header' not in g:
g.my_response_header= {}
g.my_response_header['x-custom-header'] = headers['x-custom-header']
async def call_post_api(post_body):
"""
sample method to make post api call using aiohttp clientsession
"""
try:
async with ClientSession() as session:
async with session.post(uri, headers=_headers, json=post_body) as response:
responseResult = await response.read()
_extract_headers(response, responseResult)
response_text = await response.text()
except (HttpTimeout, ConnectionError) as ex:
raise HttpTimeout(exception_message)
file: middleware.py
import flask
from flask import request, g
class SimpleMiddleWare(object):
"""
Simple WSGI middleware
"""
def __init__(self, app):
self.app = app
self._header_name = "any_request_header"
def __call__(self, environ, start_response):
"""
middleware to capture request header from incoming http request
"""
request_id_header = environ.get(self._header_name)
environ[self._header_name] = request_id_header
def new_start_response(status, response_headers, exc_info=None):
"""
set custom response headers
"""
# set the request header as response header
response_headers.append((self._header_name, request_id_header))
# this is trying to access flask.g values set in helper class & set that as response header
values = g.get(my_response_header, {})
if values.get('x-custom-header'):
response_headers.append(('x-custom-header', values.get('x-custom-header')))
return start_response(status, response_headers, exc_info)
return self.app(environ, new_start_response)
Calling the middleware from main class
file : main.py
from flask import Flask
import asyncio
from gevent.pywsgi import WSGIServer
from middleware import SimpleMiddleWare
app = Flask(__name__)
app.wsgi_app = SimpleMiddleWare(app.wsgi_app)
Upvotes: 1
Reputation: 279
This was how added my headers in my flask application and it worked perfectly
@app.after_request
def add_header(response):
response.headers['X-Content-Type-Options'] = 'nosniff'
return response
Upvotes: 24
Reputation: 350
This work for me
from flask import Flask
from flask import Response
app = Flask(__name__)
@app.route("/")
def home():
return Response(headers={'Access-Control-Allow-Origin':'*'})
if __name__ == "__main__":
app.run()
Upvotes: 13
Reputation: 132138
You can do this pretty easily:
@app.route("/")
def home():
resp = flask.Response("Foo bar baz")
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
Look at flask.Response and flask.make_response()
But something tells me you have another problem, because the after_request
should have handled it correctly too.
EDIT
I just noticed you are already using make_response
which is one of the ways to do it. Like I said before, after_request
should have worked as well. Try hitting the endpoint via curl and see what the headers are:
curl -i http://127.0.0.1:5000/your/endpoint
You should see
> curl -i 'http://127.0.0.1:5000/'
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 11
Access-Control-Allow-Origin: *
Server: Werkzeug/0.8.3 Python/2.7.5
Date: Tue, 16 Sep 2014 03:47:13 GMT
Noting the Access-Control-Allow-Origin header.
EDIT 2
As I suspected, you are getting a 500 so you are not setting the header like you thought. Try adding app.debug = True
before you start the app and try again. You should get some output showing you the root cause of the problem.
For example:
@app.route("/")
def home():
resp = flask.Response("Foo bar baz")
user.weapon = boomerang
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
Gives a nicely formatted html error page, with this at the bottom (helpful for curl command)
Traceback (most recent call last):
...
File "/private/tmp/min.py", line 8, in home
user.weapon = boomerang
NameError: global name 'boomerang' is not defined
Upvotes: 158
Reputation: 5453
Use make_response
of Flask something like
@app.route("/")
def home():
resp = make_response("hello") #here you could use make_response(render_template(...)) too
resp.headers['Access-Control-Allow-Origin'] = '*'
return resp
From flask docs,
flask.make_response(*args)
Sometimes it is necessary to set additional headers in a view. Because views do not have to return response objects but can return a value that is converted into a response object by Flask itself, it becomes tricky to add headers to it. This function can be called instead of using a return and you will get a response object which you can use to attach headers.
Upvotes: 43