From 086ab44336cd919ae6a90950f436e71b85d174a6 Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Thu, 28 Apr 2011 13:04:16 +0000 Subject: [PATCH] Fixed #15637 -- Added a require_safe decorator for views to accept GET or HEAD. Thanks, aaugustin and Julien. git-svn-id: http://code.djangoproject.com/svn/django/trunk@16115 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/views/decorators/http.py | 3 ++ docs/topics/http/decorators.txt | 60 ++++++++++++++--------- tests/regressiontests/decorators/tests.py | 26 +++++++++- 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/django/views/decorators/http.py b/django/views/decorators/http.py index dc90cc348b..054dc57c35 100644 --- a/django/views/decorators/http.py +++ b/django/views/decorators/http.py @@ -48,6 +48,9 @@ require_GET.__doc__ = "Decorator to require that a view only accept the GET meth require_POST = require_http_methods(["POST"]) require_POST.__doc__ = "Decorator to require that a view only accept the POST method." +require_safe = require_http_methods(["GET", "HEAD"]) +require_safe.__doc__ = "Decorator to require that a view only accept safe methods: GET and HEAD." + def condition(etag_func=None, last_modified_func=None): """ Decorator to support conditional retrieval (or change) for a view diff --git a/docs/topics/http/decorators.txt b/docs/topics/http/decorators.txt index 09ffecbdf9..1a5c7776b5 100644 --- a/docs/topics/http/decorators.txt +++ b/docs/topics/http/decorators.txt @@ -10,31 +10,47 @@ various HTTP features. Allowed HTTP methods ==================== -The following decorators in :mod:`django.views.decorators.http` can be used to -restrict access to views based on the request method. +The decorators in :mod:`django.views.decorators.http` can be used to restrict +access to views based on the request method. .. function:: require_http_methods(request_method_list) -This decorator is used to ensure that a view only accepts particular request -methods. Usage:: + Decorator to require that a view only accept particular request + methods. Usage:: - from django.views.decorators.http import require_http_methods + from django.views.decorators.http import require_http_methods - @require_http_methods(["GET", "POST"]) - def my_view(request): - # I can assume now that only GET or POST requests make it this far - # ... - pass + @require_http_methods(["GET", "POST"]) + def my_view(request): + # I can assume now that only GET or POST requests make it this far + # ... + pass -Note that request methods should be in uppercase. + Note that request methods should be in uppercase. .. function:: require_GET() -Decorator to require that a view only accept the GET method. + Decorator to require that a view only accept the GET method. .. function:: require_POST() -Decorator to require that a view only accept the POST method. + Decorator to require that a view only accept the POST method. + +.. function:: require_safe() + + .. versionadded:: 1.4 + + Decorator to require that a view only accept the GET and HEAD methods. + These methods are commonly considered "safe" because they should not have + the significance of taking an action other than retrieving the requested + resource. + + .. note:: + Django will automatically strip the content of responses to HEAD + requests while leaving the headers unchanged, so you may handle HEAD + requests exactly like GET requests in your views. Since some software, + such as link checkers, rely on HEAD requests, you might prefer + using ``require_safe`` instead of ``require_GET``. Conditional view processing =========================== @@ -48,9 +64,9 @@ control caching behavior on particular views. .. function:: last_modified(last_modified_func) -These decorators can be used to generate ``ETag`` and ``Last-Modified`` -headers; see -:doc:`conditional view processing `. + These decorators can be used to generate ``ETag`` and ``Last-Modified`` + headers; see + :doc:`conditional view processing `. .. module:: django.views.decorators.gzip @@ -62,9 +78,9 @@ compression on a per-view basis. .. function:: gzip_page() -This decorator compresses content if the browser allows gzip compression. -It sets the ``Vary`` header accordingly, so that caches will base their -storage on the ``Accept-Encoding`` header. + This decorator compresses content if the browser allows gzip compression. + It sets the ``Vary`` header accordingly, so that caches will base their + storage on the ``Accept-Encoding`` header. .. module:: django.views.decorators.vary @@ -78,7 +94,7 @@ caching based on specific request headers. .. function:: vary_on_headers(*headers) -The ``Vary`` header defines which request headers a cache mechanism should take -into account when building its cache key. + The ``Vary`` header defines which request headers a cache mechanism should take + into account when building its cache key. -See :ref:`using vary headers `. + See :ref:`using vary headers `. diff --git a/tests/regressiontests/decorators/tests.py b/tests/regressiontests/decorators/tests.py index dac4029904..52a1ecd909 100644 --- a/tests/regressiontests/decorators/tests.py +++ b/tests/regressiontests/decorators/tests.py @@ -2,11 +2,11 @@ from functools import wraps from django.contrib.auth.decorators import login_required, permission_required, user_passes_test from django.contrib.admin.views.decorators import staff_member_required -from django.http import HttpResponse, HttpRequest +from django.http import HttpResponse, HttpRequest, HttpResponseNotAllowed from django.utils.decorators import method_decorator from django.utils.functional import allow_lazy, lazy, memoize from django.utils.unittest import TestCase -from django.views.decorators.http import require_http_methods, require_GET, require_POST +from django.views.decorators.http import require_http_methods, require_GET, require_POST, require_safe from django.views.decorators.vary import vary_on_headers, vary_on_cookie from django.views.decorators.cache import cache_page, never_cache, cache_control @@ -20,6 +20,7 @@ fully_decorated.anything = "Expected __dict__" fully_decorated = require_http_methods(["GET"])(fully_decorated) fully_decorated = require_GET(fully_decorated) fully_decorated = require_POST(fully_decorated) +fully_decorated = require_safe(fully_decorated) # django.views.decorators.vary fully_decorated = vary_on_headers('Accept-language')(fully_decorated) @@ -111,6 +112,27 @@ class DecoratorsTest(TestCase): my_view_cached4 = cache_page()(my_view) self.assertEqual(my_view_cached4(HttpRequest()), "response") + def test_require_safe_accepts_only_safe_methods(self): + """ + Test for the require_safe decorator. + A view returns either a response or an exception. + Refs #15637. + """ + def my_view(request): + return HttpResponse("OK") + my_safe_view = require_safe(my_view) + request = HttpRequest() + request.method = 'GET' + self.assertTrue(isinstance(my_safe_view(request), HttpResponse)) + request.method = 'HEAD' + self.assertTrue(isinstance(my_safe_view(request), HttpResponse)) + request.method = 'POST' + self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed)) + request.method = 'PUT' + self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed)) + request.method = 'DELETE' + self.assertTrue(isinstance(my_safe_view(request), HttpResponseNotAllowed)) + # For testing method_decorator, a decorator that assumes a single argument. # We will get type arguments if there is a mismatch in the number of arguments.