Login

HttpMethodsMiddleware

Author:
hawkeye
Posted:
April 12, 2007
Language:
Python
Version:
.96
Tags:
middleware rest
Score:
7 (after 7 ratings)

This middleware allows developers to "fake" browser support for HTTP methods. Even though most modern browsers only support GET and POST, the HTTP standard defines others. In the context of REST, PUT and DELETE are used for client interaction with the server.

For forms with a PUT or DELETE method, this middleware will change them to go through POST, and will include an invisible field called "method_middleware_transform" that carries the originally intended method.

So, <form method="PUT" ...>...</form> More or less becomes <form method="POST" ...><input type=hidden name="method_middleware_transform" value="PUT"></form> (with a few other minor HTML modifications)

The process is completely transparent to the developer... you never have to deal with the fact that browsers don't support the standard methods.

One caveat is that server interaction via XMLHttpRequest (AJAX) requires special attention... this middleware won't properly setup your XMLHttpRequest to take advantage of this functionality.

This is a combination of the work of Jesse Lovelace and the Django CSRF middleware.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import re
import itertools

_HTML_TYPES = ('text/html', 'application/xhtml+xml')    

_SUPPORTED_TRANSFORMS = ['PUT', 'DELETE']

_FORM_RE = re.compile(r'((<form\W[^>]*\bmethod=(\'|"|))(%s)((\'|"|)\b[^>]*>))' % '|'.join(_SUPPORTED_TRANSFORMS), re.IGNORECASE)

_MIDDLEWARE_KEY = 'method_middleware_transform'
 
class HttpMethodsMiddleware(object):
    def process_request(self, request):
        if request.POST and request.POST.has_key(_MIDDLEWARE_KEY):
            if request.POST[_MIDDLEWARE_KEY].upper() in _SUPPORTED_TRANSFORMS:
                request.method = request.POST[_MIDDLEWARE_KEY]
        return None
           
    def process_response(self, request, response):
        if response['Content-Type'].split(';')[0] in _HTML_TYPES:
            # ensure we don't add the 'id' attribute twice (HTML validity)
            idattributes = itertools.chain(("id='" + _MIDDLEWARE_KEY + "'",), 
                                            itertools.repeat(''))
            def add_transform_field(match):
                """Returns the matched <form> tag with a modified method and
                the added <input> element"""
                return match.group(2) + "POST" + match.group(5) + \
                "<div style='display:none;'>" + \
                "<input type='hidden' " + idattributes.next() + \
                " name='" + _MIDDLEWARE_KEY + "' value='" + \
                match.group(4).upper() + "' /></div>"

            # Modify any POST forms
            response.content = _FORM_RE.sub(add_transform_field, response.content)
        return response

More like this

  1. caching parsed templates by forgems 7 years, 4 months ago
  2. Clear FileField/ImageField files in the Admin by marinho 5 years, 9 months ago
  3. Using class methods as views by panyam 5 years, 10 months ago
  4. FieldAccessForm (per-field user access for forms derived from models) by Killarny 6 years, 6 months ago
  5. A action decorator for URLs by Batiste 7 years, 1 month ago

Comments

peplin (on April 20, 2011):

Be aware that this snippet as is can expose your application to CSRF attacks. Django's CSRF checks will always occur after this middleware is run, and it currently only checks POST requests.

I've made some updates for our version of this snippet here (with more comments): https://github.com/bueda/django-comrade/commit/de4d38b0c503908f11d5d73a60e3beef4735997c

#

Please login first before commenting.