Is it possible automatically add Access-Control-Allow-Origin
header to all responses which was initiated by ajax request (with header X-Requested-With
) in Pyramid?
Asked
Active
Viewed 8,965 times
5 Answers
17
There are several ways to do this: 1) a custom request factory like drnextgis showed, a NewRequest event handler, or a tween. A tween is almost certainly not the right way to do this, so I won't show that. Here is the event handler version:
def add_cors_headers_response_callback(event):
def cors_headers(request, response):
response.headers.update({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Authorization',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '1728000',
})
event.request.add_response_callback(cors_headers)
from pyramid.events import NewRequest
config.add_subscriber(add_cors_headers_response_callback, NewRequest)

nnguyen
- 128
- 5

Wichert Akkerman
- 4,918
- 2
- 23
- 30
10
I've solved the problem using set_request_factory
:
from pyramid.request import Request
from pyramid.request import Response
def request_factory(environ):
request = Request(environ)
if request.is_xhr:
request.response = Response()
request.response.headerlist = []
request.response.headerlist.extend(
(
('Access-Control-Allow-Origin', '*'),
('Content-Type', 'application/json')
)
)
return request
config.set_request_factory(request_factory)

drnextgis
- 2,194
- 1
- 25
- 32
-
6Doing this opens your server to attack from *ANY* domain; It would be much more secure to include a white-list of domains instead of a single wildcard. – honestduane Mar 20 '14 at 23:56
-
2honestduane makes a very good point here. If you are creating an API for public data (lets say weather information, or public transportation data) this may be the right thigh to do. – Wichert Akkerman Apr 04 '14 at 11:55
-
This is great, but it's often not sufficient. You will generally want to support OPTIONS requests too ([implementation](https://gist.github.com/mitchellrj/3721859)) as [browsers will convert POST to OPTIONS](http://stackoverflow.com/q/12111936/509706) in some circumstances. – Wilfred Hughes Dec 10 '14 at 14:51
-
2If you use a request factory, make sure that your request handlers take the response from the request (`response = request.response`) instead of making a new one (`response = Response(...)`). In the latter case your headers will not be included. – bdesham Jan 15 '15 at 22:37
2
I fixed this with add some headers to response, by create a response callback: pyramid.events.NewResponse
cors.py
from pyramid.security import NO_PERMISSION_REQUIRED
def includeme(config):
config.add_directive(
'add_cors_preflight_handler', add_cors_preflight_handler)
config.add_route_predicate('cors_preflight', CorsPreflightPredicate)
config.add_subscriber(add_cors_to_response, 'pyramid.events.NewResponse')
class CorsPreflightPredicate(object):
def __init__(self, val, config):
self.val = val
def text(self):
return 'cors_preflight = %s' % bool(self.val)
phash = text
def __call__(self, context, request):
if not self.val:
return False
return (
request.method == 'OPTIONS' and
'HTTP_ORIGIN' in request.headers.environ and
'HTTP_ACCESS_CONTROL_REQUEST_METHOD' in request.headers.environ
)
def add_cors_preflight_handler(config):
config.add_route(
'cors-options-preflight', '/{catch_all:.*}',
cors_preflight=True,
)
config.add_view(
cors_options_view,
route_name='cors-options-preflight',
permission=NO_PERMISSION_REQUIRED,
)
def add_cors_to_response(event):
request = event.request
response = event.response
if 'HTTP_ORIGIN' in request.headers.environ:
response.headers.update({
'Access-Control-Allow-Origin': '*',
'Access-Control-Expose-Headers': 'Content-Type,Date,Content-Length,Authorization,X-Request-ID',
'Access-Control-Allow-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Accept-Language, Authorization ,X-Request-ID',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '1728000',
})
def cors_options_view(context, request):
response = request.response
if 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS' in request.headers.environ:
response.headers.update({
'Access-Control-Allow-Origin': '*',
'Access-Control-Expose-Headers': 'Content-Type,Date,Content-Length,Authorization,X-Request-ID',
'Access-Control-Allow-Methods': 'POST,GET,DELETE,PUT,OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, Accept, Accept-Language, Authorization ,X-Request-ID',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '1728000',
})
else:
response.headers['HTTP_ACCESS_CONTROL_ALLOW_HEADERS'] = (
'Origin,Content-Type,Accept,Accept-Language,Authorization,X-Request-ID')
return response
At the end add this two line on your own Configurator object :
# cors
config.include('FileManager.classes.cors')
# make sure to add this before other routes to intercept OPTIONS
config.add_cors_preflight_handler()

Javad Asoodeh
- 299
- 3
- 12
1
I could send file with Ajax from a server to another server :
import uuid
from pyramid.view import view_config
from pyramid.response import Response
class FManager:
def __init__(self, request):
self.request = request
@view_config(route_name='f_manager', request_method='POST', renderer='json')
def post(self):
file_ = self.request.POST.items()
content_type = str(file_[0][1].type).split('/')
file_[0][1].filename = str(uuid.uuid4()) + '.' + content_type[1]
file_id = self.request.storage.save(file_[0][1])
response = Response(body="{'data':'success'}")
response.headers.update({
'Access-Control-Allow-Origin': '*',
})
return response

Javad Asoodeh
- 299
- 3
- 12
1
Here is another solution:
from pyramid.events import NewResponse, subscriber
@subscriber(NewResponse)
def add_cors_headers(event):
if event.request.is_xhr:
event.response.headers.update({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET',
})

Cito
- 5,365
- 28
- 30