Pyramid CORS for Ajax requests

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

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)

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)

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()