Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for X-HTTP-Method-Override in flask.views.MethodView. #582

Closed
wants to merge 1 commit into from
Closed

Conversation

jperras
Copy link
Contributor

@jperras jperras commented Aug 27, 2012

When an incoming request contains the X-HTTP-Method-Override header, the
value of that header is used as the view method to be executed instead
of the original incoming HTTP verb.

This helps support dumb(er) webservers and proxies that don't understand newer HTTP verbs (e.g. PATCH), of which Amazon's Elastic Load Balancer service is a member.

I only added the functionality to flask.views.MethodView, because I was unsure about what would be a good way to do it with the standard dispatch_request; developers often introspect the HTTP method inside of decorator-based routes when supporting more than one verb in a route, so should the request.method value be overridden completely, or should devs be required to check for the X-HTTP-Method-Override header themselves? Neither option seems very appealing to me.

When an incoming request contains the X-HTTP-Method-Override header, the
value of that header is used as the view method to be executed instead
of the original incoming HTTP verb.
@travisbot
Copy link

This pull request passes (merged 708a2ab into 80b2689).

@kennethreitz
Copy link
Contributor

+100, I'd love to see this enabled in Flask by default. However, I believe there's a built-in way to do this with werkzeug. @mitsuhiko ?

@mattupstate
Copy link
Contributor

I'm wary of HTTP headers that start with X-. I use a WSGI middleware paired with a query string to care of this for me.

class HTTPMethodOverrideMiddleware(object):
    """The HTTPMethodOverrideMiddleware middleware implements the hidden HTTP
    method technique. Not all web browsers support every HTTP method, such as
    DELETE and PUT. Using a querystring parameter is the easiest implementation
    given Werkzeug and how middleware is implemented. The following is an
    example of how to create a form with a PUT method:

        <form action="/stuff/id?__METHOD_OVERRIDE__=PUT" method="POST">
            ...
        </form>
    """
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        if '__METHOD_OVERRIDE__' in environ.get('QUERY_STRING', ''):
            args = url_decode(environ['QUERY_STRING'])
            method = args.get('__METHOD_OVERRIDE__').upper()
            if method in ['GET', 'POST', 'PUT', 'DELETE']:
                method = method.encode('ascii', 'replace')
                environ['REQUEST_METHOD'] = method
        return self.app(environ, start_response)

@jperras
Copy link
Contributor Author

jperras commented Aug 28, 2012

@mattupstate Agreed, but there are some situations where X- headers are acceptable; the most notable example being the pseudo-standard X-Requested-With used by werkzeug to determine if the originating request originated from an XMLHttpRequest from the browser.

I do like your middleware solution idea, but I'd rather not pollute my query strings with metadata that is better represented in a header.

@mattupstate
Copy link
Contributor

In some cases it is quite convenient to use a query string, especially in plain old HTML views that POST forms. Headers are nice with clients that are more full featured. I once looked into seeing if a header value could be retrieved in the middleware but I think it would have proved difficult since the environ wasn't "parsed" yet but I'm also not very well versed in WSGI yet. I think it would be ideal if it's possible to check for either a query string or header in the middleware.

@jperras
Copy link
Contributor Author

jperras commented Sep 11, 2012

Any thoughts, @mitsuhiko?

@boris317
Copy link

Building on what @mattupstate posted here is an example that looks for an X-HTTP-Method-Override header

class HTTPMethodOverrideMiddleware(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        method = environ.get('HTTP_X_HTTP_METHOD_OVERRIDE', '').upper()
        if method in ['GET', 'POST', 'PUT', 'DELETE']:
            method = method.encode('ascii', 'replace')
            environ['REQUEST_METHOD'] = method
        return self.app(environ, start_response)

@mitsuhiko
Copy link
Contributor

This should indeed be fixed on the WSGI layer for consistency. It's easy enough to make a middleware, maybe we should put it into the docs.

@tomchristie
Copy link

The HTTP_X_HTTP_METHOD_OVERRIDE should probably only be respected if the method is POST.
Eg. As per Google API style. https://developers.google.com/gdata/docs/2.0/basics

Using HTTP_X_HTTP_METHOD_OVERRIDE with GET requests could result unexpected behavior due to caching, and I'd also be wary that it might represent a CSRF attack vector in some circumstances.

For further reference, also see @jacobian's note against this similar pull request: https://github.com/toastdriven/django-tastypie/pull/351

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants