Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #15637 -- Added a require_safe decorator for views to accept GE…

…T or HEAD. Thanks, aaugustin and Julien.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16115 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 086ab44336cd919ae6a90950f436e71b85d174a6 1 parent 013ce8a
Jannis Leidel authored April 28, 2011
3  django/views/decorators/http.py
@@ -48,6 +48,9 @@ def inner(request, *args, **kwargs):
48 48
 require_POST = require_http_methods(["POST"])
49 49
 require_POST.__doc__ = "Decorator to require that a view only accept the POST method."
50 50
 
  51
+require_safe = require_http_methods(["GET", "HEAD"])
  52
+require_safe.__doc__ = "Decorator to require that a view only accept safe methods: GET and HEAD."
  53
+
51 54
 def condition(etag_func=None, last_modified_func=None):
52 55
     """
53 56
     Decorator to support conditional retrieval (or change) for a view
60  docs/topics/http/decorators.txt
@@ -10,31 +10,47 @@ various HTTP features.
10 10
 Allowed HTTP methods
11 11
 ====================
12 12
 
13  
-The following decorators in :mod:`django.views.decorators.http` can be used to
14  
-restrict access to views based on the request method.
  13
+The decorators in :mod:`django.views.decorators.http` can be used to restrict
  14
+access to views based on the request method.
15 15
 
16 16
 .. function:: require_http_methods(request_method_list)
17 17
 
18  
-This decorator is used to ensure that a view only accepts particular request
19  
-methods. Usage::
  18
+    Decorator to require that a view only accept particular request
  19
+    methods. Usage::
20 20
 
21  
-    from django.views.decorators.http import require_http_methods
  21
+        from django.views.decorators.http import require_http_methods
22 22
 
23  
-    @require_http_methods(["GET", "POST"])
24  
-    def my_view(request):
25  
-        # I can assume now that only GET or POST requests make it this far
26  
-        # ...
27  
-        pass
  23
+        @require_http_methods(["GET", "POST"])
  24
+        def my_view(request):
  25
+            # I can assume now that only GET or POST requests make it this far
  26
+            # ...
  27
+            pass
28 28
 
29  
-Note that request methods should be in uppercase.
  29
+    Note that request methods should be in uppercase.
30 30
 
31 31
 .. function:: require_GET()
32 32
 
33  
-Decorator to require that a view only accept the GET method.
  33
+    Decorator to require that a view only accept the GET method.
34 34
 
35 35
 .. function:: require_POST()
36 36
 
37  
-Decorator to require that a view only accept the POST method.
  37
+    Decorator to require that a view only accept the POST method.
  38
+
  39
+.. function:: require_safe()
  40
+
  41
+    .. versionadded:: 1.4
  42
+
  43
+    Decorator to require that a view only accept the GET and HEAD methods.
  44
+    These methods are commonly considered "safe" because they should not have
  45
+    the significance of taking an action other than retrieving the requested
  46
+    resource.
  47
+
  48
+    .. note::
  49
+        Django will automatically strip the content of responses to HEAD
  50
+        requests while leaving the headers unchanged, so you may handle HEAD
  51
+        requests exactly like GET requests in your views. Since some software,
  52
+        such as link checkers, rely on HEAD requests, you might prefer
  53
+        using ``require_safe`` instead of ``require_GET``.
38 54
 
39 55
 Conditional view processing
40 56
 ===========================
@@ -48,9 +64,9 @@ control caching behavior on particular views.
48 64
 
49 65
 .. function:: last_modified(last_modified_func)
50 66
 
51  
-These decorators can be used to generate ``ETag`` and ``Last-Modified``
52  
-headers; see
53  
-:doc:`conditional view processing </topics/conditional-view-processing>`.
  67
+    These decorators can be used to generate ``ETag`` and ``Last-Modified``
  68
+    headers; see
  69
+    :doc:`conditional view processing </topics/conditional-view-processing>`.
54 70
 
55 71
 .. module:: django.views.decorators.gzip
56 72
 
@@ -62,9 +78,9 @@ compression on a per-view basis.
62 78
 
63 79
 .. function:: gzip_page()
64 80
 
65  
-This decorator compresses content if the browser allows gzip compression.
66  
-It sets the ``Vary`` header accordingly, so that caches will base their
67  
-storage on the ``Accept-Encoding`` header.
  81
+    This decorator compresses content if the browser allows gzip compression.
  82
+    It sets the ``Vary`` header accordingly, so that caches will base their
  83
+    storage on the ``Accept-Encoding`` header.
68 84
 
69 85
 .. module:: django.views.decorators.vary
70 86
 
@@ -78,7 +94,7 @@ caching based on specific request headers.
78 94
 
79 95
 .. function:: vary_on_headers(*headers)
80 96
 
81  
-The ``Vary`` header defines which request headers a cache mechanism should take
82  
-into account when building its cache key.
  97
+    The ``Vary`` header defines which request headers a cache mechanism should take
  98
+    into account when building its cache key.
83 99
 
84  
-See :ref:`using vary headers <using-vary-headers>`.
  100
+    See :ref:`using vary headers <using-vary-headers>`.
26  tests/regressiontests/decorators/tests.py
@@ -2,11 +2,11 @@
2 2
 
3 3
 from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
4 4
 from django.contrib.admin.views.decorators import staff_member_required
5  
-from django.http import HttpResponse, HttpRequest
  5
+from django.http import HttpResponse, HttpRequest, HttpResponseNotAllowed
6 6
 from django.utils.decorators import method_decorator
7 7
 from django.utils.functional import allow_lazy, lazy, memoize
8 8
 from django.utils.unittest import TestCase
9  
-from django.views.decorators.http import require_http_methods, require_GET, require_POST
  9
+from django.views.decorators.http import require_http_methods, require_GET, require_POST, require_safe
10 10
 from django.views.decorators.vary import vary_on_headers, vary_on_cookie
11 11
 from django.views.decorators.cache import cache_page, never_cache, cache_control
12 12
 
@@ -20,6 +20,7 @@ def fully_decorated(request):
20 20
 fully_decorated = require_http_methods(["GET"])(fully_decorated)
21 21
 fully_decorated = require_GET(fully_decorated)
22 22
 fully_decorated = require_POST(fully_decorated)
  23
+fully_decorated = require_safe(fully_decorated)
23 24
 
24 25
 # django.views.decorators.vary
25 26
 fully_decorated = vary_on_headers('Accept-language')(fully_decorated)
@@ -111,6 +112,27 @@ def my_view(request):
111 112
         my_view_cached4 = cache_page()(my_view)
112 113
         self.assertEqual(my_view_cached4(HttpRequest()), "response")
113 114
 
  115
+    def test_require_safe_accepts_only_safe_methods(self):
  116
+        """
  117
+        Test for the require_safe decorator.
  118
+        A view returns either a response or an exception.
  119
+        Refs #15637.
  120
+        """
  121
+        def my_view(request):
  122
+            return HttpResponse("OK")
  123
+        my_safe_view = require_safe(my_view)
  124
+        request = HttpRequest()
  125
+        request.method = 'GET'
  126
+        self.assertTrue(isinstance(my_safe_view(request), HttpResponse))
  127
+        request.method = 'HEAD'
  128
+        self.assertTrue(isinstance(my_safe_view(request), HttpResponse))
  129
+        request.method = 'POST'
  130
+        self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed))
  131
+        request.method = 'PUT'
  132
+        self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed))
  133
+        request.method = 'DELETE'
  134
+        self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed))
  135
+
114 136
 
115 137
 # For testing method_decorator, a decorator that assumes a single argument.
116 138
 # We will get type arguments if there is a mismatch in the number of arguments.

0 notes on commit 086ab44

Please sign in to comment.
Something went wrong with that request. Please try again.