How to use Pyramid's add_view dynamically add CORS handler views based on views we had in config?

790 views
Skip to first unread message

Fang-Pen Lin

unread,
Jun 8, 2017, 1:11:24 AM6/8/17
to pylons-discuss
Hi guys,

I have a traversal based pyramid web app, there are controllers like this

@view_defaults(
    context=UserResource,
    renderer='json',
)
class UserController(ControllerBase):
    @view_config(request_method='GET', permission='view')
    def get(self):
        user = self.context.entity
        return user

    @view_config(request_method='PUT', permission='update')
    def put(self):
        # update user here
        return user


One problem I ran into was, I found out myself in need to add handler for CORS preflight requests. The code above will be something like this

@view_defaults(
    context=UserResource,
    renderer='json',
)
class UserController(ControllerBase):

    @view_config(request_method='OPTIONS', permission='options')
    def options(self):
        return Response(b'', headerlist=[
            ('Access-Control-Allow-Origin', 'http://localhost:8080'),
            ('Access-Control-Allow-Methods', 'POST'),
            ('Access-Control-Allow-Headers', 'Origin, Content-Type, Accept, Authorization'),
        ])

    # other code goes here

Turned out I will need to add a ton of these CORS handlers. Besides handling this manually, I am thinking about deal with it programmatically. I found out there are WSGI middleware like this one


Kind of able to do what I want, but still, it's not really ideal, as it applies to the whole app. Sometimes, I want to have fine-grand control over CORS for different endpoints.

For example, I may can define something like

    @view_config(request_method='PUT', permission='update', cors_allowed_origin='http://foobar.com')
    def put(self):
        # update user here
        return user

see the "cors_allow_origin" I added to view_config for this put method. There are also other things I can add. 

I am thinking about using "Introspectable" API, enumerate all the view, and define corresponding view via add_view method of config. Like this

def define_cors_views(config):
    for view in config introspectable 
views:
                 some rules to determine what kind of preflight view to add
        config.add_view # add the preflight request handler for the view

config.add_before_commit(define_cors_views
)

Here comes the questions
  • Is there a hook or what I can use on config, to put my code for scanning views and adding corresponding views before commit?
  • Is there any code sample similar to what I want to do, that I can reference to?
  • Is this scan and add views for preflight request approach the best way to do with Pyramid? Any other better ideas?

Fang-Pen Lin

unread,
Jun 8, 2017, 1:29:10 AM6/8/17
to pylons-discuss
after looking around seems like "action" is something I want?


hmmm, also still not sure if this scan and add views is the best way to do

Michael Merickel

unread,
Jun 8, 2017, 2:26:00 AM6/8/17
to Pylons
On Thu, Jun 8, 2017 at 12:11 AM, Fang-Pen Lin <born...@gmail.com> wrote:
Kind of able to do what I want, but still, it's not really ideal, as it applies to the whole app. Sometimes, I want to have fine-grand control over CORS for different endpoints.

For example, I may can define something like

    @view_config(request_method='PUT', permission='update', cors_allowed_origin='http://foobar.com')
    def put(self):
        # update user here
        return user

see the "cors_allow_origin" I added to view_config for this put method. There are also other things I can add. 

You can do *exactly* this with view derivers [1]. You can even wrap every view in your app and then provide an opt-out on a per-view basis if you wanted. The trickiest part here is that you still need to define a view that handles pre-flight OPTIONS requests. The two best options I can think of right now are:

1. Define a global catch-all route+view and coordinate it with a view deriver in order to build up a registry of views that want CORS protections. The global route+view could use this registry to determine how to return rules for various requests.

2. Define an action like `config.add_cors_route` that would register a route and an OPTIONS view automatically. This would avoid needing anything defined on the views themselves. For non-preflight requests you can add a subscriber that adds headers to the responses generated by these routes.

As far as implementing the CORS state machine, I have a gist [2] that I wrote a while ago and have used successfully in some apps. It works globally (probably similar to wsgicors) but it may serve as a good a example of things to do when making a per-route or per-view solution.

Reply all
Reply to author
Forward
0 new messages