diff options
| author | Florian Apolloner <florian@apolloner.eu> | 2015-11-07 16:12:37 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-05-17 07:22:22 -0400 |
| commit | 9baf692a58de78dba13aa582098781675367c329 (patch) | |
| tree | 1926555441d0c3b13185782dce193b839d616a4a | |
| parent | 05c888ffb843ba3eff06cd07b3cef5bbb513a54f (diff) | |
Fixed #26601 -- Improved middleware per DEP 0005.
Thanks Tim Graham for polishing the patch, updating the tests, and
writing documentation. Thanks Carl Meyer for shepherding the DEP.
81 files changed, 898 insertions, 1412 deletions
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 1b71b1b1b8..09af2fcda4 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -433,14 +433,16 @@ SECURE_PROXY_SSL_HEADER = None # MIDDLEWARE # ############## -# List of middleware classes to use. Order is important; in the request phase, -# this middleware classes will be applied in the order given, and in the -# response phase the middleware will be applied in reverse order. +# List of middleware to use. Order is important; in the request phase, these +# middleware will be applied in the order given, and in the response +# phase the middleware will be applied in reverse order. MIDDLEWARE_CLASSES = [ 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', ] +MIDDLEWARE = None + ############ # SESSIONS # ############ diff --git a/django/conf/project_template/project_name/settings.py-tpl b/django/conf/project_template/project_name/settings.py-tpl index 3ef0ab7900..7dfe186929 100644 --- a/django/conf/project_template/project_name/settings.py-tpl +++ b/django/conf/project_template/project_name/settings.py-tpl @@ -39,7 +39,7 @@ INSTALLED_APPS = [ 'django.contrib.staticfiles', ] -MIDDLEWARE_CLASSES = [ +MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py index e569df396b..7043ed80b8 100644 --- a/django/contrib/admin/tests.py +++ b/django/contrib/admin/tests.py @@ -1,19 +1,18 @@ from django.contrib.staticfiles.testing import StaticLiveServerTestCase from django.test import modify_settings from django.test.selenium import SeleniumTestCase +from django.utils.deprecation import MiddlewareMixin from django.utils.translation import ugettext as _ -class CSPMiddleware(object): +class CSPMiddleware(MiddlewareMixin): """The admin's JavaScript should be compatible with CSP.""" def process_response(self, request, response): response['Content-Security-Policy'] = "default-src 'self'" return response -@modify_settings( - MIDDLEWARE_CLASSES={'append': 'django.contrib.admin.tests.CSPMiddleware'}, -) +@modify_settings(MIDDLEWARE={'append': 'django.contrib.admin.tests.CSPMiddleware'}) class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): available_apps = [ diff --git a/django/contrib/admindocs/middleware.py b/django/contrib/admindocs/middleware.py index 2e4cede0b9..4bdd4f45b5 100644 --- a/django/contrib/admindocs/middleware.py +++ b/django/contrib/admindocs/middleware.py @@ -1,8 +1,9 @@ from django import http from django.conf import settings +from django.utils.deprecation import MiddlewareMixin -class XViewMiddleware(object): +class XViewMiddleware(MiddlewareMixin): """ Adds an X-View header to internal HEAD requests -- used by the documentation system. """ @@ -15,8 +16,11 @@ class XViewMiddleware(object): """ assert hasattr(request, 'user'), ( "The XView middleware requires authentication middleware to be " - "installed. Edit your MIDDLEWARE_CLASSES setting to insert " - "'django.contrib.auth.middleware.AuthenticationMiddleware'.") + "installed. Edit your MIDDLEWARE%s setting to insert " + "'django.contrib.auth.middleware.AuthenticationMiddleware'." % ( + "_CLASSES" if settings.MIDDLEWARE is None else "" + ) + ) if request.method == 'HEAD' and (request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS or (request.user.is_active and request.user.is_staff)): response = http.HttpResponse() diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index 476729c147..8c56e645fe 100644 --- a/django/contrib/auth/middleware.py +++ b/django/contrib/auth/middleware.py @@ -1,7 +1,9 @@ +from django.conf import settings from django.contrib import auth from django.contrib.auth import load_backend from django.contrib.auth.backends import RemoteUserBackend from django.core.exceptions import ImproperlyConfigured +from django.utils.deprecation import MiddlewareMixin from django.utils.functional import SimpleLazyObject @@ -11,18 +13,18 @@ def get_user(request): return request._cached_user -class AuthenticationMiddleware(object): +class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): assert hasattr(request, 'session'), ( "The Django authentication middleware requires session middleware " - "to be installed. Edit your MIDDLEWARE_CLASSES setting to insert " + "to be installed. Edit your MIDDLEWARE%s setting to insert " "'django.contrib.sessions.middleware.SessionMiddleware' before " "'django.contrib.auth.middleware.AuthenticationMiddleware'." - ) + ) % ("_CLASSES" if settings.MIDDLEWARE is None else "") request.user = SimpleLazyObject(lambda: get_user(request)) -class SessionAuthenticationMiddleware(object): +class SessionAuthenticationMiddleware(MiddlewareMixin): """ Formerly, a middleware for invalidating a user's sessions that don't correspond to the user's current session authentication hash. However, it @@ -35,7 +37,7 @@ class SessionAuthenticationMiddleware(object): pass -class RemoteUserMiddleware(object): +class RemoteUserMiddleware(MiddlewareMixin): """ Middleware for utilizing Web-server-provided authentication. @@ -61,7 +63,7 @@ class RemoteUserMiddleware(object): raise ImproperlyConfigured( "The Django remote user auth middleware requires the" " authentication middleware to be installed. Edit your" - " MIDDLEWARE_CLASSES setting to insert" + " MIDDLEWARE setting to insert" " 'django.contrib.auth.middleware.AuthenticationMiddleware'" " before the RemoteUserMiddleware class.") try: diff --git a/django/contrib/flatpages/forms.py b/django/contrib/flatpages/forms.py index 19f3b6309c..fbc7da04de 100644 --- a/django/contrib/flatpages/forms.py +++ b/django/contrib/flatpages/forms.py @@ -29,8 +29,9 @@ class FlatpageForm(forms.ModelForm): ugettext("URL is missing a leading slash."), code='missing_leading_slash', ) - if (settings.APPEND_SLASH and - 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES and + if (settings.APPEND_SLASH and ( + (settings.MIDDLEWARE and 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE) or + 'django.middleware.common.CommonMiddleware' in settings.MIDDLEWARE_CLASSES) and not url.endswith('/')): raise forms.ValidationError( ugettext("URL is missing a trailing slash."), diff --git a/django/contrib/flatpages/middleware.py b/django/contrib/flatpages/middleware.py index 32d881ffa4..4c6760535d 100644 --- a/django/contrib/flatpages/middleware.py +++ b/django/contrib/flatpages/middleware.py @@ -1,9 +1,20 @@ from django.conf import settings from django.contrib.flatpages.views import flatpage from django.http import Http404 +from django.middleware.exception import ExceptionMiddleware -class FlatpageFallbackMiddleware(object): +class FlatpageFallbackMiddleware(ExceptionMiddleware): + + def __init__(self, get_response=None): + # This override makes get_response optional during the + # MIDDLEWARE_CLASSES deprecation. + super(FlatpageFallbackMiddleware, self).__init__(get_response) + + def __call__(self, request): + response = super(FlatpageFallbackMiddleware, self).__call__(request) + return self.process_response(request, response) + def process_response(self, request, response): if response.status_code != 404: return response # No need to check for a flatpage for non-404 responses. diff --git a/django/contrib/messages/middleware.py b/django/contrib/messages/middleware.py index a84dc6cb11..4cdbf63c64 100644 --- a/django/contrib/messages/middleware.py +++ b/django/contrib/messages/middleware.py @@ -1,8 +1,9 @@ from django.conf import settings from django.contrib.messages.storage import default_storage +from django.utils.deprecation import MiddlewareMixin -class MessageMiddleware(object): +class MessageMiddleware(MiddlewareMixin): """ Middleware that handles temporary messages. """ diff --git a/django/contrib/messages/storage/session.py b/django/contrib/messages/storage/session.py index 7903fb03c1..624de42b8c 100644 --- a/django/contrib/messages/storage/session.py +++ b/django/contrib/messages/storage/session.py @@ -1,5 +1,6 @@ import json +from django.conf import settings from django.contrib.messages.storage.base import BaseStorage from django.contrib.messages.storage.cookie import ( MessageDecoder, MessageEncoder, @@ -17,7 +18,7 @@ class SessionStorage(BaseStorage): assert hasattr(request, 'session'), "The session-based temporary "\ "message storage requires session middleware to be installed, "\ "and come before the message middleware in the "\ - "MIDDLEWARE_CLASSES list." + "MIDDLEWARE%s list." % ("_CLASSES" if settings.MIDDLEWARE is None else "") super(SessionStorage, self).__init__(request, *args, **kwargs) def _get(self, *args, **kwargs): diff --git a/django/contrib/redirects/middleware.py b/django/contrib/redirects/middleware.py index dc8d94bc96..e3ee982a8b 100644 --- a/django/contrib/redirects/middleware.py +++ b/django/contrib/redirects/middleware.py @@ -6,20 +6,26 @@ from django.conf import settings from django.contrib.redirects.models import Redirect from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ImproperlyConfigured +from django.middleware.exception import ExceptionMiddleware -class RedirectFallbackMiddleware(object): +class RedirectFallbackMiddleware(ExceptionMiddleware): # Defined as class-level attributes to be subclassing-friendly. response_gone_class = http.HttpResponseGone response_redirect_class = http.HttpResponsePermanentRedirect - def __init__(self): + def __init__(self, get_response=None): if not apps.is_installed('django.contrib.sites'): raise ImproperlyConfigured( "You cannot use RedirectFallbackMiddleware when " "django.contrib.sites is not installed." ) + super(RedirectFallbackMiddleware, self).__init__(get_response) + + def __call__(self, request): + response = super(RedirectFallbackMiddleware, self).__call__(request) + return self.process_response(request, response) def process_response(self, request, response): # No need to check for a redirect for non-404 responses. diff --git a/django/contrib/sessions/middleware.py b/django/contrib/sessions/middleware.py index 73ffe5a2a2..4871b48075 100644 --- a/django/contrib/sessions/middleware.py +++ b/django/contrib/sessions/middleware.py @@ -5,11 +5,13 @@ from django.conf import settings from django.contrib.sessions.backends.base import UpdateError from django.shortcuts import redirect from django.utils.cache import patch_vary_headers +from django.utils.deprecation import MiddlewareMixin from django.utils.http import cookie_date -class SessionMiddleware(object): - def __init__(self): +class SessionMiddleware(MiddlewareMixin): + def __init__(self, get_response=None): + self.get_response = get_response engine = import_module(settings.SESSION_ENGINE) self.SessionStore = engine.SessionStore diff --git a/django/core/checks/security/base.py b/django/core/checks/security/base.py index eacbb90843..eee7f854c8 100644 --- a/django/core/checks/security/base.py +++ b/django/core/checks/security/base.py @@ -1,13 +1,14 @@ from django.conf import settings from .. import Tags, Warning, register +from ..utils import patch_middleware_message SECRET_KEY_MIN_LENGTH = 50 SECRET_KEY_MIN_UNIQUE_CHARACTERS = 5 W001 = Warning( "You do not have 'django.middleware.security.SecurityMiddleware' " - "in your MIDDLEWARE_CLASSES so the SECURE_HSTS_SECONDS, " + "in your MIDDLEWARE so the SECURE_HSTS_SECONDS, " "SECURE_CONTENT_TYPE_NOSNIFF, " "SECURE_BROWSER_XSS_FILTER, and SECURE_SSL_REDIRECT settings " "will have no effect.", @@ -17,7 +18,7 @@ W001 = Warning( W002 = Warning( "You do not have " "'django.middleware.clickjacking.XFrameOptionsMiddleware' in your " - "MIDDLEWARE_CLASSES, so your pages will not be served with an " + "MIDDLEWARE, so your pages will not be served with an " "'x-frame-options' header. Unless there is a good reason for your " "site to be served in a frame, you should consider enabling this " "header to help prevent clickjacking attacks.", @@ -88,7 +89,7 @@ W018 = Warning( W019 = Warning( "You have " "'django.middleware.clickjacking.XFrameOptionsMiddleware' in your " - "MIDDLEWARE_CLASSES, but X_FRAME_OPTIONS is not set to 'DENY'. " + "MIDDLEWARE, but X_FRAME_OPTIONS is not set to 'DENY'. " "The default is 'SAMEORIGIN', but unless there is a good reason for " "your site to serve other parts of itself in a frame, you should " "change it to 'DENY'.", @@ -102,23 +103,25 @@ W020 = Warning( def _security_middleware(): - return "django.middleware.security.SecurityMiddleware" in settings.MIDDLEWARE_CLASSES + return ("django.middleware.security.SecurityMiddleware" in settings.MIDDLEWARE_CLASSES or + settings.MIDDLEWARE and "django.middleware.security.SecurityMiddleware" in settings.MIDDLEWARE) def _xframe_middleware(): - return "django.middleware.clickjacking.XFrameOptionsMiddleware" in settings.MIDDLEWARE_CLASSES + return ("django.middleware.clickjacking.XFrameOptionsMiddleware" in settings.MIDDLEWARE_CLASSES or + settings.MIDDLEWARE and "django.middleware.clickjacking.XFrameOptionsMiddleware" in settings.MIDDLEWARE) @register(Tags.security, deploy=True) def check_security_middleware(app_configs, **kwargs): passed_check = _security_middleware() - return [] if passed_check else [W001] + return [] if passed_check else [patch_middleware_message(W001)] @register(Tags.security, deploy=True) def check_xframe_options_middleware(app_configs, **kwargs): passed_check = _xframe_middleware() - return [] if passed_check else [W002] + return [] if passed_check else [patch_middleware_message(W002)] @register(Tags.security, deploy=True) @@ -186,7 +189,7 @@ def check_xframe_deny(app_configs, **kwargs): not _xframe_middleware() or settings.X_FRAME_OPTIONS == 'DENY' ) - return [] if passed_check else [W019] + return [] if passed_check else [patch_middleware_message(W019)] @register(Tags.security, deploy=True) diff --git a/django/core/checks/security/csrf.py b/django/core/checks/security/csrf.py index 3effbc4498..b90308a016 100644 --- a/django/core/checks/security/csrf.py +++ b/django/core/checks/security/csrf.py @@ -1,19 +1,20 @@ from django.conf import settings from .. import Tags, Warning, register +from ..utils import patch_middleware_message W003 = Warning( "You don't appear to be using Django's built-in " "cross-site request forgery protection via the middleware " "('django.middleware.csrf.CsrfViewMiddleware' is not in your " - "MIDDLEWARE_CLASSES). Enabling the middleware is the safest approach " + "MIDDLEWARE). Enabling the middleware is the safest approach " "to ensure you don't leave any holes.", id='security.W003', ) W016 = Warning( "You have 'django.middleware.csrf.CsrfViewMiddleware' in your " - "MIDDLEWARE_CLASSES, but you have not set CSRF_COOKIE_SECURE to True. " + "MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. " "Using a secure-only CSRF cookie makes it more difficult for network " "traffic sniffers to steal the CSRF token.", id='security.W016', @@ -21,7 +22,7 @@ W016 = Warning( W017 = Warning( "You have 'django.middleware.csrf.CsrfViewMiddleware' in your " - "MIDDLEWARE_CLASSES, but you have not set CSRF_COOKIE_HTTPONLY to True. " + "MIDDLEWARE, but you have not set CSRF_COOKIE_HTTPONLY to True. " "Using an HttpOnly CSRF cookie makes it more difficult for cross-site " "scripting attacks to steal the CSRF token.", id='security.W017', @@ -29,13 +30,14 @@ W017 = Warning( def _csrf_middleware(): - return "django.middleware.csrf.CsrfViewMiddleware" in settings.MIDDLEWARE_CLASSES + return ("django.middleware.csrf.CsrfViewMiddleware" in settings.MIDDLEWARE_CLASSES or + settings.MIDDLEWARE and "django.middleware.csrf.CsrfViewMiddleware" in settings.MIDDLEWARE) @register(Tags.security, deploy=True) def check_csrf_middleware(app_configs, **kwargs): passed_check = _csrf_middleware() - return [] if passed_check else [W003] + return [] if passed_check else [patch_middleware_message(W003)] @register(Tags.security, deploy=True) @@ -44,7 +46,7 @@ def check_csrf_cookie_secure(app_configs, **kwargs): not _csrf_middleware() or settings.CSRF_COOKIE_SECURE ) - return [] if passed_check else [W016] + return [] if passed_check else [patch_middleware_message(W016)] @register(Tags.security, deploy=True) @@ -53,4 +55,4 @@ def check_csrf_cookie_httponly(app_configs, **kwargs): not _csrf_middleware() or settings.CSRF_COOKIE_HTTPONLY ) - return [] if passed_check else [W017] + return [] if passed_check else [patch_middleware_message(W017)] diff --git a/django/core/checks/security/sessions.py b/django/core/checks/security/sessions.py index 7e857d74d8..5fae4ed4d9 100644 --- a/django/core/checks/security/sessions.py +++ b/django/core/checks/security/sessions.py @@ -1,6 +1,7 @@ from django.conf import settings from .. import Tags, Warning, register +from ..utils import patch_middleware_message def add_session_cookie_message(message): @@ -20,7 +21,7 @@ W010 = Warning( W011 = Warning( add_session_cookie_message( "You have 'django.contrib.sessions.middleware.SessionMiddleware' " - "in your MIDDLEWARE_CLASSES, but you have not set " + "in your MIDDLEWARE, but you have not set " "SESSION_COOKIE_SECURE to True." ), id='security.W011', @@ -50,7 +51,7 @@ W013 = Warning( W014 = Warning( add_httponly_message( "You have 'django.contrib.sessions.middleware.SessionMiddleware' " - "in your MIDDLEWARE_CLASSES, but you have not set " + "in your MIDDLEWARE, but you have not set " "SESSION_COOKIE_HTTPONLY to True." ), id='security.W014', @@ -69,7 +70,7 @@ def check_session_cookie_secure(app_configs, **kwargs): if _session_app(): errors.append(W010) if _session_middleware(): - errors.append(W011) + errors.append(patch_middleware_message(W011)) if len(errors) > 1: errors = [W012] return errors @@ -82,15 +83,15 @@ def check_session_cookie_httponly(app_configs, **kwargs): if _session_app(): errors.append(W013) if _session_middleware(): - errors.append(W014) + errors.append(patch_middleware_message(W014)) if len(errors) > 1: errors = [W015] return errors def _session_middleware(): - return ("django.contrib.sessions.middleware.SessionMiddleware" in - settings.MIDDLEWARE_CLASSES) + return ("django.contrib.sessions.middleware.SessionMiddleware" in settings.MIDDLEWARE_CLASSES or + settings.MIDDLEWARE and "django.contrib.sessions.middleware.SessionMiddleware" in settings.MIDDLEWARE) def _session_app(): diff --git a/django/core/checks/utils.py b/django/core/checks/utils.py new file mode 100644 index 0000000000..995d8432c9 --- /dev/null +++ b/django/core/checks/utils.py @@ -0,0 +1,10 @@ +import copy + +from django.conf import settings + + +def patch_middleware_message(error): + if settings.MIDDLEWARE is None: + error = copy.copy(error) + error.msg = error.msg.replace('MIDDLEWARE', 'MIDDLEWARE_CLASSES') + return error diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index 65cb59357c..b206c70b05 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -7,7 +7,7 @@ import warnings from django.conf import settings from django.core import signals -from django.core.exceptions import MiddlewareNotUsed +from django.core.exceptions import ImproperlyConfigured, MiddlewareNotUsed from django.db import connections, transaction from django.middleware.exception import ExceptionMiddleware from django.urls import get_resolver, get_urlconf, set_urlconf @@ -31,7 +31,8 @@ class BaseHandler(object): def load_middleware(self): """ - Populate middleware lists from settings.MIDDLEWARE_CLASSES. + Populate middleware lists from settings.MIDDLEWARE (or the deprecated + MIDDLEWARE_CLASSES). Must be called after the environment is fixed (see __call__ in subclasses). """ @@ -41,29 +42,55 @@ class BaseHandler(object): self._response_middleware = [] self._exception_middleware = [] - handler = self._legacy_get_response - for middleware_path in settings.MIDDLEWARE_CLASSES: - mw_class = import_string(middleware_path) - try: - mw_instance = mw_class() - except MiddlewareNotUsed as exc: - if settings.DEBUG: - if six.text_type(exc): - logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) - else: - logger.debug('MiddlewareNotUsed: %r', middleware_path) - continue + if settings.MIDDLEWARE is None: + handler = self._legacy_get_response + for middleware_path in settings.MIDDLEWARE_CLASSES: + mw_class = import_string(middleware_path) + try: + mw_instance = mw_class() + except MiddlewareNotUsed as exc: + if settings.DEBUG: + if six.text_type(exc): + logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) + else: + logger.debug('MiddlewareNotUsed: %r', middleware_path) + continue + + if hasattr(mw_instance, 'process_request'): + self._request_middleware.append(mw_instance.process_request) + if hasattr(mw_instance, 'process_view'): + self._view_middleware.append(mw_instance.process_view) + if hasattr(mw_instance, 'process_template_response'): + self._template_response_middleware.insert(0, mw_instance.process_template_response) + if hasattr(mw_instance, 'process_response'): + self._response_middleware.insert(0, mw_instance.process_response) + if hasattr(mw_instance, 'process_exception'): + self._exception_middleware.insert(0, mw_instance.process_exception) + else: + handler = self._get_response + for middleware_path in reversed(settings.MIDDLEWARE): + middleware = import_string(middleware_path) + try: + mw_instance = middleware(handler) + except MiddlewareNotUsed as exc: + if settings.DEBUG: + if six.text_type(exc): + logger.debug('MiddlewareNotUsed(%r): %s', middleware_path, exc) + else: + logger.debug('MiddlewareNotUsed: %r', middleware_path) + continue + + if mw_instance is None: + raise ImproperlyConfigured( + 'Middleware factory %s returned None.' % middleware_path + ) + + if hasattr(mw_instance, 'process_view'): + self._view_middleware.insert(0, mw_instance.process_view) + if hasattr(mw_instance, 'process_template_response'): + self._template_response_middleware.append(mw_instance.process_template_response) - if hasattr(mw_instance, 'process_request'): - self._request_middleware.append(mw_instance.process_request) - if hasattr(mw_instance, 'process_view'): - self._view_middleware.append(mw_instance.process_view) - if hasattr(mw_instance, 'process_template_response'): - self._template_response_middleware.insert(0, mw_instance.process_template_response) - if hasattr(mw_instance, 'process_response'): - self._response_middleware.insert(0, mw_instance.process_response) - if hasattr(mw_instance, 'process_exception'): - self._exception_middleware.insert(0, mw_instance.process_exception) + handler = mw_instance handler = ExceptionMiddleware(handler, self) diff --git a/django/middleware/cache.py b/django/middleware/cache.py index 11a65cd928..8ee3f514e8 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -4,7 +4,7 @@ URL. The canonical way to enable cache middleware is to set ``UpdateCacheMiddleware`` as your first piece of middleware, and ``FetchFromCacheMiddleware`` as the last:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', ... 'django.middleware.cache.FetchFromCacheMiddleware' @@ -49,22 +49,24 @@ from django.utils.cache import ( get_cache_key, get_max_age, has_vary_header, learn_cache_key, patch_response_headers, ) +from django.utils.deprecation import MiddlewareMixin -class UpdateCacheMiddleware(object): +class UpdateCacheMiddleware(MiddlewareMixin): """ Response-phase cache middleware that updates the cache if the response is cacheable. Must be used as part of the two-part update/fetch cache middleware. - UpdateCacheMiddleware must be the first piece of middleware in - MIDDLEWARE_CLASSES so that it'll get called last during the response phase. + UpdateCacheMiddleware must be the first piece of middleware in MIDDLEWARE + so that it'll get called last during the response phase. """ - def __init__(self): + def __init__(self, get_response=None): self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS self.cache = caches[self.cache_alias] + self.get_response = get_response def _should_update_cache(self, request, response): return hasattr(request, '_cache_update_cache') and request._cache_update_cache @@ -104,18 +106,19 @@ class UpdateCacheMiddleware(object): return response -class FetchFromCacheMiddleware(object): +class FetchFromCacheMiddleware(MiddlewareMixin): """ Request-phase cache middleware that fetches a page from the cache. Must be used as part of the two-part update/fetch cache middleware. - FetchFromCacheMiddleware must be the last piece of middleware in - MIDDLEWARE_CLASSES so that it'll get called last during the request phase. + FetchFromCacheMiddleware must be the last piece of middleware in MIDDLEWARE + so that it'll get called last during the request phase. """ - def __init__(self): + def __init__(self, get_response=None): self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS self.cache = caches[self.cache_alias] + self.get_response = get_response def process_request(self, request): """ @@ -153,7 +156,8 @@ class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware): Also used as the hook point for the cache decorator, which is generated using the decorator-from-middleware utility. """ - def __init__(self, cache_timeout=None, **kwargs): + def __init__(self, get_response=None, cache_timeout=None, **kwargs): + self.get_response = get_response # We need to differentiate between "provided, but using default value", # and "not provided". If the value is provided using a default, then # we fall back to system defaults. If it is not provided at all, diff --git a/django/middleware/clickjacking.py b/django/middleware/clickjacking.py index acd0705703..8659646c61 100644 --- a/django/middleware/clickjacking.py +++ b/django/middleware/clickjacking.py @@ -6,9 +6,10 @@ malicious site loading resources from your site in a hidden frame. """ from django.conf import settings +from django.utils.deprecation import MiddlewareMixin -class XFrameOptionsMiddleware(object): +class XFrameOptionsMiddleware(MiddlewareMixin): """ Middleware that sets the X-Frame-Options HTTP header in HTTP responses. diff --git a/django/middleware/common.py b/django/middleware/common.py index b7db74a018..4cec6f0e41 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -7,6 +7,7 @@ from django.core.exceptions import PermissionDenied from django.core.mail import mail_managers from django.urls import is_valid_path from django.utils.cache import get_conditional_response, set_response_etag +from django.utils.deprecation import MiddlewareMixin from django.utils.encoding import force_text from django.utils.http import unquote_etag from django.utils.six.moves.urllib.parse import urlparse @@ -14,7 +15,7 @@ from django.utils.six.moves.urllib.parse import urlparse logger = logging.getLogger('django.request') -class CommonMiddleware(object): +class CommonMiddleware(MiddlewareMixin): """ "Common" middleware for taking care of some basic operations: @@ -129,7 +130,7 @@ class CommonMiddleware(object): return response -class BrokenLinkEmailsMiddleware(object): +class BrokenLinkEmailsMiddleware(MiddlewareMixin): def process_response(self, request, response): """ diff --git a/django/middleware/csrf.py b/django/middleware/csrf.py index f2d6a49708..ba9f63ec3d 100644 --- a/django/middleware/csrf.py +++ b/django/middleware/csrf.py @@ -13,6 +13,7 @@ from django.conf import settings from django.urls import get_callable from django.utils.cache import patch_vary_headers from django.utils.crypto import constant_time_compare, get_random_string +from django.utils.deprecation import MiddlewareMixin from django.utils.encoding import force_text from django.utils.http import is_same_domain from django.utils.six.moves.urllib.parse import urlparse @@ -78,7 +79,7 @@ def _sanitize_token(token): return token -class CsrfViewMiddleware(object): +class CsrfViewMiddleware(MiddlewareMixin): """ Middleware that requires a present and correct csrfmiddlewaretoken for POST requests that have a CSRF cookie, and sets an outgoing diff --git a/django/middleware/gzip.py b/django/middleware/gzip.py index e772cb326d..642e3188d3 100644 --- a/django/middleware/gzip.py +++ b/django/middleware/gzip.py @@ -1,12 +1,13 @@ import re from django.utils.cache import patch_vary_headers +from django.utils.deprecation import MiddlewareMixin from django.utils.text import compress_sequence, compress_string re_accepts_gzip = re.compile(r'\bgzip\b') -class GZipMiddleware(object): +class GZipMiddleware(MiddlewareMixin): """ This middleware compresses content if the browser allows gzip compression. It sets the Vary header accordingly, so that caches will base their storage diff --git a/django/middleware/http.py b/django/middleware/http.py index 7912200634..a3030463c4 100644 --- a/django/middleware/http.py +++ b/django/middleware/http.py @@ -1,8 +1,9 @@ from django.utils.cache import get_conditional_response +from django.utils.deprecation import MiddlewareMixin from django.utils.http import http_date, parse_http_date_safe, unquote_etag -class ConditionalGetMiddleware(object): +class ConditionalGetMiddleware(MiddlewareMixin): """ Handles conditional GET operations. If the response has an ETag or Last-Modified header, and the request has If-None-Match or diff --git a/django/middleware/locale.py b/django/middleware/locale.py index f8b16b8475..1a95dbc8b2 100644 --- a/django/middleware/locale.py +++ b/django/middleware/locale.py @@ -3,12 +3,13 @@ from django.conf import settings from django.conf.urls.i18n import is_language_prefix_patterns_used from django.http import HttpResponseRedirect +from django.middleware.exception import ExceptionMiddleware from django.urls import get_script_prefix, is_valid_path from django.utils import translation from django.utils.cache import patch_vary_headers -class LocaleMiddleware(object): +class LocaleMiddleware(ExceptionMiddleware): """ This is a very simple middleware that parses a request and decides what translation object to install in the current @@ -18,6 +19,17 @@ class LocaleMiddleware(object): """ response_redirect_class = HttpResponseRedirect + def __init__(self, get_response=None): + # This override makes get_response optional during the + # MIDDLEWARE_CLASSES deprecation. + super(LocaleMiddleware, self).__init__(get_response) + + def __call__(self, request): + response = self.process_request(request) + if not response: + response = super(LocaleMiddleware, self).__call__(request) + return self.process_response(request, response) + def process_request(self, request): urlconf = getattr(request, 'urlconf', settings.ROOT_URLCONF) i18n_patterns_used, prefixed_default_language = is_language_prefix_patterns_used(urlconf) diff --git a/django/middleware/security.py b/django/middleware/security.py index 46afb68f57..02a59ad6e4 100644 --- a/django/middleware/security.py +++ b/django/middleware/security.py @@ -2,10 +2,11 @@ import re from django.conf import settings from django.http import HttpResponsePermanentRedirect +from django.utils.deprecation import MiddlewareMixin -class SecurityMiddleware(object): - def __init__(self): +class SecurityMiddleware(MiddlewareMixin): + def __init__(self, get_response=None): self.sts_seconds = settings.SECURE_HSTS_SECONDS self.sts_include_subdomains = settings.SECURE_HSTS_INCLUDE_SUBDOMAINS self.content_type_nosniff = settings.SECURE_CONTENT_TYPE_NOSNIFF @@ -13,6 +14,7 @@ class SecurityMiddleware(object): self.redirect = settings.SECURE_SSL_REDIRECT self.redirect_host = settings.SECURE_SSL_HOST self.redirect_exempt = [re.compile(r) for r in settings.SECURE_REDIRECT_EXEMPT] + self.get_response = get_response def process_request(self, request): path = request.path.lstrip("/") diff --git a/django/test/client.py b/django/test/client.py index e6a3650701..dc813716d6 100644 --- a/django/test/client.py +++ b/django/test/client.py @@ -125,7 +125,7 @@ class ClientHandler(BaseHandler): def __call__(self, environ): # Set up middleware if needed. We couldn't do this earlier, because # settings weren't available. - if self._request_middleware is None: + if self._middleware_chain is None: self.load_middleware() request_started.disconnect(close_old_connections) diff --git a/django/utils/deprecation.py b/django/utils/deprecation.py index 70216e8c5b..7627426080 100644 --- a/django/utils/deprecation.py +++ b/django/utils/deprecation.py @@ -109,3 +109,25 @@ class CallableBool: CallableFalse = CallableBool(False) CallableTrue = CallableBool(True) + + +class MiddlewareMixin(object): + def __init__(self, get_response=None): + self.get_response = get_response + super(MiddlewareMixin, self).__init__() + + def __call__(self, request): + response = None + if hasattr(self, 'process_request'): + response = self.process_request(request) + if not response: + try: + response = self.get_response(request) + except Exception as e: + if hasattr(self, 'process_exception'): + return self.process_exception(request, e) + else: + raise + if hasattr(self, 'process_response'): + response = self.process_response(request, response) + return response diff --git a/django/views/debug.py b/django/views/debug.py index 73fedce68d..56329325d3 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -852,7 +852,8 @@ Python Version: {{ sys_version_info }} Installed Applications: {{ settings.INSTALLED_APPS|pprint }} Installed Middleware: -{{ settings.MIDDLEWARE_CLASSES|pprint }} +{% if settings.MIDDLEWARE is not None %}{{ settings.MIDDLEWARE|pprint }}""" +"""{% else %}{{ settings.MIDDLEWARE_CLASSES|pprint }}{% endif %} {% if template_does_not_exist %}Template loader postmortem {% if postmortem %}Django tried loading these templates, in this order: @@ -1059,7 +1060,8 @@ Server time: {{server_time|date:"r"}} Installed Applications: {{ settings.INSTALLED_APPS|pprint }} Installed Middleware: -{{ settings.MIDDLEWARE_CLASSES|pprint }} +{% if settings.MIDDLEWARE is not None %}{{ settings.MIDDLEWARE|pprint }}""" +"""{% else %}{{ settings.MIDDLEWARE_CLASSES|pprint }}{% endif %} {% if template_does_not_exist %}Template loader postmortem {% if postmortem %}Django tried loading these templates, in this order: {% for entry in postmortem %} diff --git a/docs/howto/auth-remote-user.txt b/docs/howto/auth-remote-user.txt index 1680baa92b..5bfe210458 100644 --- a/docs/howto/auth-remote-user.txt +++ b/docs/howto/auth-remote-user.txt @@ -29,10 +29,10 @@ Configuration First, you must add the :class:`django.contrib.auth.middleware.RemoteUserMiddleware` to the -:setting:`MIDDLEWARE_CLASSES` setting **after** the +:setting:`MIDDLEWARE` setting **after** the :class:`django.contrib.auth.middleware.AuthenticationMiddleware`:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ '...', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware', diff --git a/docs/howto/error-reporting.txt b/docs/howto/error-reporting.txt index d07404a1e9..7a51338539 100644 --- a/docs/howto/error-reporting.txt +++ b/docs/howto/error-reporting.txt @@ -57,7 +57,7 @@ not found" errors). Django sends emails about 404 errors when: * :setting:`DEBUG` is ``False``; -* Your :setting:`MIDDLEWARE_CLASSES` setting includes +* Your :setting:`MIDDLEWARE` setting includes :class:`django.middleware.common.BrokenLinkEmailsMiddleware`. If those conditions are met, Django will email the users listed in the @@ -78,7 +78,7 @@ behavior is from broken Web bots too. before other middleware that intercepts 404 errors, such as :class:`~django.middleware.locale.LocaleMiddleware` or :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware`. - Put it towards the top of your :setting:`MIDDLEWARE_CLASSES` setting. + Put it towards the top of your :setting:`MIDDLEWARE` setting. You can tell Django to stop reporting particular 404s by tweaking the :setting:`IGNORABLE_404_URLS` setting. It should be a list of compiled diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt index be761505fe..53d9bcd9b5 100644 --- a/docs/ref/applications.txt +++ b/docs/ref/applications.txt @@ -37,8 +37,8 @@ projects. Applications include some combination of models, views, templates, template tags, static files, URLs, middleware, etc. They're generally wired into projects with the :setting:`INSTALLED_APPS` setting and optionally with other -mechanisms such as URLconfs, the :setting:`MIDDLEWARE_CLASSES` setting, or -template inheritance. +mechanisms such as URLconfs, the :setting:`MIDDLEWARE` setting, or template +inheritance. It is important to understand that a Django application is just a set of code that interacts with various parts of the framework. There's no such thing as diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index e4af02c1a2..5057ef18b6 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -491,19 +491,19 @@ The following checks are run if you use the :option:`check --deploy` option: * **security.W001**: You do not have :class:`django.middleware.security.SecurityMiddleware` in your - :setting:`MIDDLEWARE_CLASSES` so the :setting:`SECURE_HSTS_SECONDS`, + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES` so the :setting:`SECURE_HSTS_SECONDS`, :setting:`SECURE_CONTENT_TYPE_NOSNIFF`, :setting:`SECURE_BROWSER_XSS_FILTER`, and :setting:`SECURE_SSL_REDIRECT` settings will have no effect. * **security.W002**: You do not have :class:`django.middleware.clickjacking.XFrameOptionsMiddleware` in your - :setting:`MIDDLEWARE_CLASSES`, so your pages will not be served with an + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES`, so your pages will not be served with an ``'x-frame-options'`` header. Unless there is a good reason for your site to be served in a frame, you should consider enabling this header to help prevent clickjacking attacks. * **security.W003**: You don't appear to be using Django's built-in cross-site request forgery protection via the middleware (:class:`django.middleware.csrf.CsrfViewMiddleware` is not in your - :setting:`MIDDLEWARE_CLASSES`). Enabling the middleware is the safest + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES`). Enabling the middleware is the safest approach to ensure you don't leave any holes. * **security.W004**: You have not set a value for the :setting:`SECURE_HSTS_SECONDS` setting. If your entire site is served only @@ -540,7 +540,7 @@ The following checks are run if you use the :option:`check --deploy` option: sessions. * **security.W011**: You have :class:`django.contrib.sessions.middleware.SessionMiddleware` in your - :setting:`MIDDLEWARE_CLASSES`, but you have not set + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES`, but you have not set :setting:`SESSION_COOKIE_SECURE` to ``True``. Using a secure-only session cookie makes it more difficult for network traffic sniffers to hijack user sessions. @@ -554,7 +554,7 @@ The following checks are run if you use the :option:`check --deploy` option: sessions. * **security.W014**: You have :class:`django.contrib.sessions.middleware.SessionMiddleware` in your - :setting:`MIDDLEWARE_CLASSES`, but you have not set + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES`, but you have not set :setting:`SESSION_COOKIE_HTTPONLY` to ``True``. Using an ``HttpOnly`` session cookie makes it more difficult for cross-site scripting attacks to hijack user sessions. @@ -571,7 +571,7 @@ The following checks are run if you use the :option:`check --deploy` option: deployment. * **security.W019**: You have :class:`django.middleware.clickjacking.XFrameOptionsMiddleware` in your - :setting:`MIDDLEWARE_CLASSES`, but :setting:`X_FRAME_OPTIONS` is not set to + :setting:`MIDDLEWARE`/:setting:`MIDDLEWARE_CLASSES`, but :setting:`X_FRAME_OPTIONS` is not set to ``'DENY'``. The default is ``'SAMEORIGIN'``, but unless there is a good reason for your site to serve other parts of itself in a frame, you should change it to ``'DENY'``. diff --git a/docs/ref/clickjacking.txt b/docs/ref/clickjacking.txt index abac81dcd8..2f72e18d2f 100644 --- a/docs/ref/clickjacking.txt +++ b/docs/ref/clickjacking.txt @@ -56,9 +56,9 @@ Setting ``X-Frame-Options`` for all responses To set the same ``X-Frame-Options`` value for all responses in your site, put ``'django.middleware.clickjacking.XFrameOptionsMiddleware'`` to -:setting:`MIDDLEWARE_CLASSES`:: +:setting:`MIDDLEWARE`:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ ... 'django.middleware.clickjacking.XFrameOptionsMiddleware', ... diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index eed7d4451c..66b116ad35 100644 --- a/docs/ref/contrib/admin/index.txt +++ b/docs/ref/contrib/admin/index.txt @@ -41,8 +41,8 @@ For reference, here are the requirements: defined in your :setting:`TEMPLATES` as well as :class:`django.contrib.auth.middleware.AuthenticationMiddleware` and :class:`django.contrib.messages.middleware.MessageMiddleware` to - :setting:`MIDDLEWARE_CLASSES`. (These are all active by default, so - you only need to do this if you've manually tweaked the settings.) + :setting:`MIDDLEWARE`. These are all active by default, so you only need to + do this if you've manually tweaked the settings. 4. Determine which of your application's models should be editable in the admin interface. diff --git a/docs/ref/contrib/flatpages.txt b/docs/ref/contrib/flatpages.txt index 3326a93ea8..580f864c34 100644 --- a/docs/ref/contrib/flatpages.txt +++ b/docs/ref/contrib/flatpages.txt @@ -53,7 +53,7 @@ Then either: or: 3. Add ``'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware'`` - to your :setting:`MIDDLEWARE_CLASSES` setting. + to your :setting:`MIDDLEWARE` setting. 4. Run the command :djadmin:`manage.py migrate <migrate>`. @@ -144,8 +144,7 @@ can do all of the work. methods. Only requests which are successfully routed to a view via normal URL resolution apply view middleware. -Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you -can put +Note that the order of :setting:`MIDDLEWARE` matters. Generally, you can put :class:`~django.contrib.flatpages.middleware.FlatpageFallbackMiddleware` at the end of the list. This means it will run first when processing the response, and ensures that any other response-processing middlewares see the real flatpage diff --git a/docs/ref/contrib/messages.txt b/docs/ref/contrib/messages.txt index d425899cd9..414cf21116 100644 --- a/docs/ref/contrib/messages.txt +++ b/docs/ref/contrib/messages.txt @@ -27,14 +27,14 @@ already contains all the settings required to enable message functionality: * ``'django.contrib.messages'`` is in :setting:`INSTALLED_APPS`. -* :setting:`MIDDLEWARE_CLASSES` contains +* :setting:`MIDDLEWARE` contains ``'django.contrib.sessions.middleware.SessionMiddleware'`` and ``'django.contrib.messages.middleware.MessageMiddleware'``. The default :ref:`storage backend <message-storage-backends>` relies on :doc:`sessions </topics/http/sessions>`. That's why ``SessionMiddleware`` must be enabled and appear before ``MessageMiddleware`` in - :setting:`MIDDLEWARE_CLASSES`. + :setting:`MIDDLEWARE`. * The ``'context_processors'`` option of the ``DjangoTemplates`` backend defined in your :setting:`TEMPLATES` setting contains @@ -42,8 +42,8 @@ already contains all the settings required to enable message functionality: If you don't want to use messages, you can remove ``'django.contrib.messages'`` from your :setting:`INSTALLED_APPS`, the -``MessageMiddleware`` line from :setting:`MIDDLEWARE_CLASSES`, and the -``messages`` context processor from :setting:`TEMPLATES`. +``MessageMiddleware`` line from :setting:`MIDDLEWARE`, and the ``messages`` +context processor from :setting:`TEMPLATES`. Configuring the message engine ============================== diff --git a/docs/ref/contrib/redirects.txt b/docs/ref/contrib/redirects.txt index 2aadf8a45c..8af48ba8b2 100644 --- a/docs/ref/contrib/redirects.txt +++ b/docs/ref/contrib/redirects.txt @@ -18,7 +18,7 @@ To install the redirects app, follow these steps: :ref:`is installed <enabling-the-sites-framework>`. 2. Add ``'django.contrib.redirects'`` to your :setting:`INSTALLED_APPS` setting. 3. Add ``'django.contrib.redirects.middleware.RedirectFallbackMiddleware'`` - to your :setting:`MIDDLEWARE_CLASSES` setting. + to your :setting:`MIDDLEWARE` setting. 4. Run the command :djadmin:`manage.py migrate <migrate>`. How it works @@ -49,9 +49,9 @@ given ``old_path`` with a site ID that corresponds to the The middleware only gets activated for 404s -- not for 500s or responses of any other status code. -Note that the order of :setting:`MIDDLEWARE_CLASSES` matters. Generally, you -can put :class:`~django.contrib.redirects.middleware.RedirectFallbackMiddleware` -at the end of the list, because it's a last resort. +Note that the order of :setting:`MIDDLEWARE` matters. Generally, you can put +:class:`~django.contrib.redirects.middleware.RedirectFallbackMiddleware` at the +end of the list, because it's a last resort. For more on middleware, read the :doc:`middleware docs </topics/http/middleware>`. diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt index 2f312d04e0..ec2c1bf2b2 100644 --- a/docs/ref/contrib/sites.txt +++ b/docs/ref/contrib/sites.txt @@ -405,8 +405,8 @@ If you often use this pattern:: there is simple way to avoid repetitions. Add :class:`django.contrib.sites.middleware.CurrentSiteMiddleware` to -:setting:`MIDDLEWARE_CLASSES`. The middleware sets the ``site`` attribute on -every request object, so you can use ``request.site`` to get the current site. +:setting:`MIDDLEWARE`. The middleware sets the ``site`` attribute on every +request object, so you can use ``request.site`` to get the current site. How Django uses the sites framework =================================== diff --git a/docs/ref/csrf.txt b/docs/ref/csrf.txt index f59b45646d..277fd85720 100644 --- a/docs/ref/csrf.txt +++ b/docs/ref/csrf.txt @@ -27,10 +27,10 @@ How to use it To take advantage of CSRF protection in your views, follow these steps: -1. The CSRF middleware is activated by default in the - :setting:`MIDDLEWARE_CLASSES` setting. If you override that setting, remember - that ``'django.middleware.csrf.CsrfViewMiddleware'`` should come before any - view middleware that assume that CSRF attacks have been dealt with. +1. The CSRF middleware is activated by default in the :setting:`MIDDLEWARE` + setting. If you override that setting, remember that + ``'django.middleware.csrf.CsrfViewMiddleware'`` should come before any view + middleware that assume that CSRF attacks have been dealt with. If you disabled it, which is not recommended, you can use :func:`~django.views.decorators.csrf.csrf_protect` on particular views diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt index 49062d3474..1e582d5724 100644 --- a/docs/ref/middleware.txt +++ b/docs/ref/middleware.txt @@ -77,6 +77,36 @@ issued by the middleware. * Sends broken link notification emails to :setting:`MANAGERS` (see :doc:`/howto/error-reporting`). +Exception middleware +-------------------- + +.. module:: django.middleware.exception + :synopsis: Middleware to return responses for exceptions. + +.. class:: ExceptionMiddleware + +.. versionadded:: 1.10 + +Catches exceptions raised during the request/response cycle and returns the +appropriate response. + +* :class:`~django.http.Http404` is processed by + :data:`~django.conf.urls.handler404` (or a more friendly debug page if + :setting:`DEBUG=True <DEBUG>`). +* :class:`~django.core.exceptions.PermissionDenied` is processed + by :data:`~django.conf.urls.handler403`. +* ``MultiPartParserError`` is processed by :data:`~django.conf.urls.handler400`. +* :class:`~django.core.exceptions.SuspiciousOperation` is processed by + :data:`~django.conf.urls.handler400` (or a more friendly debug page if + :setting:`DEBUG=True <DEBUG>`). +* Any other exception is processed by :data:`~django.conf.urls.handler500` + (or a more friendly debug page if :setting:`DEBUG=True <DEBUG>`). + +Django uses this middleware regardless of whether or not you include it in +:setting:`MIDDLEWARE`, however, you may want to subclass if your own middleware +needs to transform any of these exceptions into the appropriate responses. +:class:`~django.middleware.locale.LocaleMiddleware` does this, for example. + GZip middleware --------------- diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 64bd5960cd..96f5300f8b 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -176,9 +176,9 @@ All attributes should be considered read-only, unless stated otherwise. An instance of :class:`~django.urls.ResolverMatch` representing the resolved URL. This attribute is only set after URL resolving took place, - which means it's available in all views but not in middleware methods - which are executed before URL resolving takes place (like - ``process_request()``, you can use ``process_view()`` instead). + which means it's available in all views but not in middleware which are + executed before URL resolving takes place (you can use it in + :meth:`process_view` though). Attributes set by application code ---------------------------------- @@ -210,7 +210,7 @@ Attributes set by middleware Some of the middleware included in Django's contrib apps set attributes on the request. If you don't see the attribute on a request, be sure the appropriate -middleware class is listed in :setting:`MIDDLEWARE_CLASSES`. +middleware class is listed in :setting:`MIDDLEWARE`. .. attribute:: HttpRequest.session @@ -257,7 +257,9 @@ Methods behind multiple proxies. One solution is to use middleware to rewrite the proxy headers, as in the following example:: - class MultipleProxyMiddleware(object): + from django.django.utils.deprecation import MiddlewareMixin + + class MultipleProxyMiddleware(MiddlewareMixin): FORWARDED_FOR_FIELDS = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_HOST', diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index 838dde808a..a7801f715c 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -77,7 +77,7 @@ can be used as a subdomain wildcard: ``'.example.com'`` will match ``example.com``. A value of ``'*'`` will match anything; in this case you are responsible to provide your own validation of the ``Host`` header (perhaps in a middleware; if so this middleware must be listed first in -:setting:`MIDDLEWARE_CLASSES`). +:setting:`MIDDLEWARE`). Django also allows the `fully qualified domain name (FQDN)`_ of any entries. Some browsers include a trailing dot in the ``Host`` header which Django @@ -1844,6 +1844,17 @@ Example: ``"http://media.example.com/"`` :setting:`MEDIA_URL` and :setting:`STATIC_URL` must have different values. See :setting:`MEDIA_ROOT` for more details. +.. setting:: MIDDLEWARE + +``MIDDLEWARE`` +-------------- + +.. versionadded:: 1.10 + +Default:: ``None`` + +A list of middleware to use. See :doc:`/topics/http/middleware`. + .. setting:: MIDDLEWARE_CLASSES ``MIDDLEWARE_CLASSES`` @@ -1856,7 +1867,11 @@ Default:: 'django.middleware.csrf.CsrfViewMiddleware', ] -A list of middleware classes to use. See :doc:`/topics/http/middleware`. +A list of middleware classes to use. This was the default setting used in +Django 1.9 and earlier. Django 1.10 introduced a new style of middleware. If +you have an older project using this setting you should :ref:`update any +middleware you've written yourself <upgrading-middleware>` to the new style +and then use the :setting:`MIDDLEWARE` setting. .. setting:: MIGRATION_MODULES @@ -3312,6 +3327,7 @@ HTTP * :setting:`DISALLOWED_USER_AGENTS` * :setting:`FORCE_SCRIPT_NAME` * :setting:`INTERNAL_IPS` +* :setting:`MIDDLEWARE` * :setting:`MIDDLEWARE_CLASSES` * Security diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt index f95ff1017f..1b527277af 100644 --- a/docs/ref/utils.txt +++ b/docs/ref/utils.txt @@ -173,6 +173,10 @@ The functions defined in this module share the following properties: middleware functionality on a per-view basis. The middleware is created with no params passed. + It assumes middleware that's compatible with the old style of Django 1.9 + and earlier (having methods like ``process_request()``, + ``process_exception()``, and ``process_response()``). + .. function:: decorator_from_middleware_with_args(middleware_class) Like ``decorator_from_middleware``, but returns a function diff --git a/docs/releases/1.10.txt b/docs/releases/1.10.txt index b6ac9f5c89..13e5f44d5b 100644 --- a/docs/releases/1.10.txt +++ b/docs/releases/1.10.txt @@ -37,6 +37,17 @@ It also now includes trigram support, using the :lookup:`trigram_similar` lookup, and the :class:`~django.contrib.postgres.search.TrigramSimilarity` and :class:`~django.contrib.postgres.search.TrigramDistance` expressions. +New-style middleware +-------------------- + +:doc:`A new style of middleware is introduced </topics/http/middleware>` to +solve the lack of strict request/response layering of the old-style of +middleware described in `DEP 0005 +<https://github.com/django/deps/blob/master/final/0005-improved-middleware.rst>`_. +You'll need to :ref:`adapt old, custom middleware <upgrading-middleware>` and +switch from the ``MIDDLEWARE_CLASSES`` setting to the new :setting:`MIDDLEWARE` +setting to take advantage of the improvements. + Official support for Unicode usernames -------------------------------------- diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt index 3ecdcca12e..21da0dca61 100644 --- a/docs/releases/1.5.txt +++ b/docs/releases/1.5.txt @@ -188,8 +188,7 @@ is an iterator. Since :class:`~django.http.StreamingHttpResponse` does not have a ``content`` attribute, middleware that needs access to the response content must test for -streaming responses and behave accordingly. See :ref:`response-middleware` for -more information. +streaming responses and behave accordingly. ``{% verbatim %}`` template tag ------------------------------- diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index 45526eb831..7e6794a97b 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -838,7 +838,7 @@ Session invalidation on password change ``SessionAuthenticationMiddleware`` is enabled. In older versions, this protection only applies if ``django.contrib.auth.middleware.SessionAuthenticationMiddleware`` - is enabled in :setting:`MIDDLEWARE_CLASSES`. + is enabled in :setting:`MIDDLEWARE`. If your :setting:`AUTH_USER_MODEL` inherits from :class:`~django.contrib.auth.models.AbstractBaseUser` or implements its own diff --git a/docs/topics/auth/index.txt b/docs/topics/auth/index.txt index b57c2e7436..3224af74c7 100644 --- a/docs/topics/auth/index.txt +++ b/docs/topics/auth/index.txt @@ -60,7 +60,7 @@ startproject <startproject>`, these consist of two items listed in your </ref/contrib/contenttypes>`, which allows permissions to be associated with models you create. -and these items in your :setting:`MIDDLEWARE_CLASSES` setting: +and these items in your :setting:`MIDDLEWARE` setting: 1. :class:`~django.contrib.sessions.middleware.SessionMiddleware` manages :doc:`sessions </topics/http/sessions>` across requests. diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt index ad353e65e0..d955b853b4 100644 --- a/docs/topics/cache.txt +++ b/docs/topics/cache.txt @@ -447,9 +447,9 @@ Once the cache is set up, the simplest way to use caching is to cache your entire site. You'll need to add ``'django.middleware.cache.UpdateCacheMiddleware'`` and ``'django.middleware.cache.FetchFromCacheMiddleware'`` to your -:setting:`MIDDLEWARE_CLASSES` setting, as in this example:: +:setting:`MIDDLEWARE` setting, as in this example:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', @@ -459,7 +459,7 @@ entire site. You'll need to add No, that's not a typo: the "update" middleware must be first in the list, and the "fetch" middleware must be last. The details are a bit obscure, but - see `Order of MIDDLEWARE_CLASSES`_ below if you'd like the full story. + see `Order of MIDDLEWARE`_ below if you'd like the full story. Then, add the following required settings to your Django settings file: @@ -1217,11 +1217,11 @@ Example:: def myview(request): ... -Order of ``MIDDLEWARE_CLASSES`` -=============================== +Order of ``MIDDLEWARE`` +======================= If you use caching middleware, it's important to put each half in the right -place within the :setting:`MIDDLEWARE_CLASSES` setting. That's because the cache +place within the :setting:`MIDDLEWARE` setting. That's because the cache middleware needs to know which headers by which to vary the cache storage. Middleware always adds something to the ``Vary`` response header when it can. diff --git a/docs/topics/http/_images/middleware.graffle b/docs/topics/http/_images/middleware.graffle deleted file mode 100644 index cdefd4b810..0000000000 --- a/docs/topics/http/_images/middleware.graffle +++ /dev/null @@ -1,957 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>ActiveLayerIndex</key> - <integer>0</integer> - <key>ApplicationVersion</key> - <array> - <string>com.omnigroup.OmniGrafflePro</string> - <string>139.16.0.171715</string> - </array> - <key>AutoAdjust</key> - <true/> - <key>BackgroundGraphic</key> - <dict> - <key>Bounds</key> - <string>{{0, 0}, {559.28997802734375, 782.8900146484375}}</string> - <key>Class</key> - <string>SolidGraphic</string> - <key>ID</key> - <integer>2</integer> - <key>Style</key> - <dict> - <key>shadow</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - <key>stroke</key> - <dict> - <key>Draws</key> - <string>NO</string> - </dict> - </dict> - </dict> - <key>BaseZoom</key> - <integer>0</integer> - <key>CanvasOrigin</key> - <string>{0, 0}</string> - <key>ColumnAlign</key> - <integer>1</integer> - <key>ColumnSpacing</key> - <real>36</real> - <key>CreationDate</key> - <string>2012-12-09 18:55:12 +0000</string> - <key>Creator</key> - <string>Aymeric Augustin</string> - <key>DisplayScale</key> - <string>1.000 cm = 1.000 cm</string> - <key>GraphDocumentVersion</key> - <integer>8</integer> - <key>GraphicsList</key> - <array> - <dict> - <key>Bounds</key> - <string>{{144, 405}, {369, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>33</integer> - <key>Shape</key> - <string>Bezier</string> - <key>ShapeData</key> - <dict> - <key>UnitPoints</key> - <array> - <string>{-0.5, -0.5}</string> - <string>{-0.5, -0.5}</string> - <string>{0.47959183673469341, -0.5}</string> - <string>{0.47959183673469408, -0.5}</string> - <string>{0.47959183673469341, -0.5}</string> - <string>{0.5, 0}</string> - <string>{0.5, 0}</string> - <string>{0.5, 0}</string> - <string>{0.47959183673469408, 0.5}</string> - <string>{0.47959183673469408, 0.5}</string> - <string>{0.47959183673469408, 0.5}</string> - <string>{-0.5, 0.5}</string> - <string>{-0.5, 0.5}</string> - <string>{-0.5, 0.5}</string> - <string>{-0.47560975609756084, 0}</string> - <string>{-0.47560975609756084, 0}</string> - <string>{-0.47560975609756084, 0}</string> - <string>{-0.5, -0.5}</string> - </array> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fswiss\fcharset0 Helvetica;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 view function}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{229.5, 238.5}, {297, 36}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>31</integer> - <key>Rotation</key> - <real>270</real> - <key>Shape</key> - <string>AdjustableArrow</string> - <key>ShapeData</key> - <dict> - <key>width</key> - <real>27</real> - </dict> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.8</string> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - <key>MiddleFraction</key> - <real>0.70634919404983521</real> - </dict> - <key>shadow</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.4</string> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>0</string> - </dict> - <key>Draws</key> - <string>NO</string> - <key>Fuzziness</key> - <real>0.0</real> - <key>ShadowVector</key> - <string>{0, 2}</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>1</string> - </dict> - <key>Pattern</key> - <integer>1</integer> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;\red255\green0\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf2 process_exception}</string> - </dict> - <key>TextRelativeArea</key> - <string>{{0.125, 0.25}, {0.75, 0.5}}</string> - <key>isConnectedShape</key> - <true/> - </dict> - <dict> - <key>Bounds</key> - <string>{{328.5, 229.5}, {315, 36}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>30</integer> - <key>Rotation</key> - <real>270</real> - <key>Shape</key> - <string>AdjustableArrow</string> - <key>ShapeData</key> - <dict> - <key>width</key> - <real>27</real> - </dict> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.8</string> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - <key>MiddleFraction</key> - <real>0.70634919404983521</real> - </dict> - <key>shadow</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.4</string> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>0</string> - </dict> - <key>Draws</key> - <string>NO</string> - <key>Fuzziness</key> - <real>0.0</real> - <key>ShadowVector</key> - <string>{0, 2}</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0.501961</string> - <key>r</key> - <string>0</string> - </dict> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf2 process_response}</string> - </dict> - <key>TextRelativeArea</key> - <string>{{0.125, 0.25}, {0.75, 0.5}}</string> - <key>isConnectedShape</key> - <true/> - </dict> - <dict> - <key>Bounds</key> - <string>{{283.5, 238.5}, {297, 36}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>29</integer> - <key>Rotation</key> - <real>270</real> - <key>Shape</key> - <string>AdjustableArrow</string> - <key>ShapeData</key> - <dict> - <key>width</key> - <real>27</real> - </dict> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.8</string> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - <key>MiddleFraction</key> - <real>0.70634919404983521</real> - </dict> - <key>shadow</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.4</string> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>0</string> - </dict> - <key>Draws</key> - <string>NO</string> - <key>Fuzziness</key> - <real>0.0</real> - <key>ShadowVector</key> - <string>{0, 2}</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0.501961</string> - <key>r</key> - <string>0</string> - </dict> - <key>Pattern</key> - <integer>1</integer> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf2 process_template_response}</string> - </dict> - <key>TextRelativeArea</key> - <string>{{0.125, 0.25}, {0.75, 0.5}}</string> - <key>isConnectedShape</key> - <true/> - </dict> - <dict> - <key>Bounds</key> - <string>{{27, 243}, {288, 36}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>28</integer> - <key>Rotation</key> - <real>90</real> - <key>Shape</key> - <string>AdjustableArrow</string> - <key>ShapeData</key> - <dict> - <key>width</key> - <real>27</real> - </dict> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.8</string> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - <key>MiddleFraction</key> - <real>0.70634919404983521</real> - </dict> - <key>shadow</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.4</string> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>0</string> - </dict> - <key>Draws</key> - <string>NO</string> - <key>Fuzziness</key> - <real>0.0</real> - <key>ShadowVector</key> - <string>{0, 2}</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0.501961</string> - <key>r</key> - <string>0</string> - </dict> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf2 process_view}</string> - </dict> - <key>TextRelativeArea</key> - <string>{{0.125, 0.25}, {0.75, 0.5}}</string> - <key>isConnectedShape</key> - <true/> - </dict> - <dict> - <key>Bounds</key> - <string>{{-40.500000000767386, 220.49999999804004}, {297, 36}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>27</integer> - <key>Rotation</key> - <real>90</real> - <key>Shape</key> - <string>AdjustableArrow</string> - <key>ShapeData</key> - <dict> - <key>width</key> - <real>27</real> - </dict> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.8</string> - <key>b</key> - <string>1</string> - <key>g</key> - <string>1</string> - <key>r</key> - <string>1</string> - </dict> - <key>MiddleFraction</key> - <real>0.70634919404983521</real> - </dict> - <key>shadow</key> - <dict> - <key>Color</key> - <dict> - <key>a</key> - <string>0.4</string> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0</string> - <key>r</key> - <string>0</string> - </dict> - <key>Draws</key> - <string>NO</string> - <key>Fuzziness</key> - <real>0.0</real> - <key>ShadowVector</key> - <string>{0, 2}</string> - </dict> - <key>stroke</key> - <dict> - <key>Color</key> - <dict> - <key>b</key> - <string>0</string> - <key>g</key> - <string>0.501961</string> - <key>r</key> - <string>0</string> - </dict> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;\red0\green128\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf2 process_request}</string> - </dict> - <key>TextRelativeArea</key> - <string>{{0.125, 0.25}, {0.75, 0.5}}</string> - <key>isConnectedShape</key> - <true/> - </dict> - <dict> - <key>Bounds</key> - <string>{{360, 63}, {144, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>12</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 HttpResponse}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 63}, {144, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>11</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 HttpRequest}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 324}, {432, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>10</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>FillType</key> - <integer>2</integer> - <key>GradientAngle</key> - <real>90</real> - <key>GradientColor</key> - <dict> - <key>w</key> - <string>0.666667</string> - </dict> - </dict> - <key>stroke</key> - <dict> - <key>CornerRadius</key> - <real>5</real> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 MessageMiddleware}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 279}, {432, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>9</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>FillType</key> - <integer>2</integer> - <key>GradientAngle</key> - <real>90</real> - <key>GradientColor</key> - <dict> - <key>w</key> - <string>0.666667</string> - </dict> - </dict> - <key>stroke</key> - <dict> - <key>CornerRadius</key> - <real>5</real> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 AuthenticationMiddleware}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 234}, {432, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>8</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>FillType</key> - <integer>2</integer> - <key>GradientAngle</key> - <real>90</real> - <key>GradientColor</key> - <dict> - <key>w</key> - <string>0.666667</string> - </dict> - </dict> - <key>stroke</key> - <dict> - <key>CornerRadius</key> - <real>5</real> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 CsrfViewMiddleware}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 189}, {432, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>7</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>FillType</key> - <integer>2</integer> - <key>GradientAngle</key> - <real>90</real> - <key>GradientColor</key> - <dict> - <key>w</key> - <string>0.666667</string> - </dict> - </dict> - <key>stroke</key> - <dict> - <key>CornerRadius</key> - <real>5</real> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 SessionMiddleware}</string> - </dict> - </dict> - <dict> - <key>Bounds</key> - <string>{{72, 144}, {432, 27}}</string> - <key>Class</key> - <string>ShapedGraphic</string> - <key>ID</key> - <integer>6</integer> - <key>Magnets</key> - <array> - <string>{0, 1}</string> - <string>{0, -1}</string> - <string>{1, 0}</string> - <string>{-1, 0}</string> - </array> - <key>Shape</key> - <string>Rectangle</string> - <key>Style</key> - <dict> - <key>fill</key> - <dict> - <key>FillType</key> - <integer>2</integer> - <key>GradientAngle</key> - <real>90</real> - <key>GradientColor</key> - <dict> - <key>w</key> - <string>0.666667</string> - </dict> - </dict> - <key>stroke</key> - <dict> - <key>CornerRadius</key> - <real>5</real> - </dict> - </dict> - <key>Text</key> - <dict> - <key>Text</key> - <string>{\rtf1\ansi\ansicpg1252\cocoartf1187\cocoasubrtf340 -\cocoascreenfonts1{\fonttbl\f0\fmodern\fcharset0 Courier;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs24 \cf0 CommonMiddleware}</string> - </dict> - </dict> - </array> - <key>GridInfo</key> - <dict> - <key>ShowsGrid</key> - <string>YES</string> - <key>SnapsToGrid</key> - <string>YES</string> - </dict> - <key>GuidesLocked</key> - <string>NO</string> - <key>GuidesVisible</key> - <string>YES</string> - <key>HPages</key> - <integer>1</integer> - <key>ImageCounter</key> - <integer>1</integer> - <key>KeepToScale</key> - <false/> - <key>Layers</key> - <array> - <dict> - <key>Lock</key> - <string>NO</string> - <key>Name</key> - <string>Calque 1</string> - <key>Print</key> - <string>YES</string> - <key>View</key> - <string>YES</string> - </dict> - </array> - <key>LayoutInfo</key> - <dict> - <key>Animate</key> - <string>NO</string> - <key>circoMinDist</key> - <real>18</real> - <key>circoSeparation</key> - <real>0.0</real> - <key>layoutEngine</key> - <string>dot</string> - <key>neatoSeparation</key> - <real>0.0</real> - <key>twopiSeparation</key> - <real>0.0</real> - </dict> - <key>LinksVisible</key> - <string>NO</string> - <key>MagnetsVisible</key> - <string>NO</string> - <key>MasterSheets</key> - <array/> - <key>ModificationDate</key> - <string>2012-12-09 19:48:54 +0000</string> - <key>Modifier</key> - <string>Aymeric Augustin</string> - <key>NotesVisible</key> - <string>NO</string> - <key>Orientation</key> - <integer>2</integer> - <key>OriginVisible</key> - <string>NO</string> - <key>PageBreaks</key> - <string>YES</string> - <key>PrintInfo</key> - <dict> - <key>NSBottomMargin</key> - <array> - <string>float</string> - <string>41</string> - </array> - <key>NSHorizonalPagination</key> - <array> - <string>coded</string> - <string>BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG</string> - </array> - <key>NSLeftMargin</key> - <array> - <string>float</string> - <string>18</string> - </array> - <key>NSPaperSize</key> - <array> - <string>size</string> - <string>{595.28997802734375, 841.8900146484375}</string> - </array> - <key>NSPrintReverseOrientation</key> - <array> - <string>int</string> - <string>0</string> - </array> - <key>NSRightMargin</key> - <array> - <string>float</string> - <string>18</string> - </array> - <key>NSTopMargin</key> - <array> - <string>float</string> - <string>18</string> - </array> - </dict> - <key>PrintOnePage</key> - <false/> - <key>ReadOnly</key> - <string>NO</string> - <key>RowAlign</key> - <integer>1</integer> - <key>RowSpacing</key> - <real>36</real> - <key>SheetTitle</key> - <string>Canevas 1</string> - <key>SmartAlignmentGuidesActive</key> - <string>YES</string> - <key>SmartDistanceGuidesActive</key> - <string>YES</string> - <key>UniqueID</key> - <integer>1</integer> - <key>UseEntirePage</key> - <false/> - <key>VPages</key> - <integer>1</integer> - <key>WindowInfo</key> - <dict> - <key>CurrentSheet</key> - <integer>0</integer> - <key>ExpandedCanvases</key> - <array/> - <key>Frame</key> - <string>{{248, 4}, {694, 874}}</string> - <key>ListView</key> - <true/> - <key>OutlineWidth</key> - <integer>142</integer> - <key>RightSidebar</key> - <false/> - <key>ShowRuler</key> - <true/> - <key>Sidebar</key> - <true/> - <key>SidebarWidth</key> - <integer>120</integer> - <key>VisibleRegion</key> - <string>{{0, 0}, {559, 735}}</string> - <key>Zoom</key> - <real>1</real> - <key>ZoomValues</key> - <array> - <array> - <string>Canevas 1</string> - <real>1</real> - <real>1</real> - </array> - </array> - </dict> -</dict> -</plist> diff --git a/docs/topics/http/_images/middleware.svg b/docs/topics/http/_images/middleware.svg deleted file mode 100644 index 4da8d22da8..0000000000 --- a/docs/topics/http/_images/middleware.svg +++ /dev/null @@ -1,3 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="52 47 481 409" width="481pt" height="409pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-12-09 19:48Z</dc:date><!-- Produced by OmniGraffle Professional 5.4.2 --></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="3.488"/><feOffset in="blur" result="offset" dx="0" dy="4"/><feFlood flood-color="black" flood-opacity=".75" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><linearGradient x1="0" x2="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaa"/></linearGradient><linearGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(288 144) rotate(90) scale(27)"/><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><linearGradient id="Obj_Gradient_2" xl:href="#Gradient" gradientTransform="translate(288 189) rotate(90) scale(27)"/><linearGradient id="Obj_Gradient_3" xl:href="#Gradient" gradientTransform="translate(288 234) rotate(90) scale(27)"/><linearGradient id="Obj_Gradient_4" xl:href="#Gradient" gradientTransform="translate(288 279) rotate(90) scale(27)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient" gradientTransform="translate(288 324) rotate(90) scale(27)"/><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canevas 1</title><rect fill="white" width="559.28998" height="782.89"/><g><title>Calque 1</title><g><use xl:href="#id6_Graphic" filter="url(#Shadow)"/><use xl:href="#id7_Graphic" filter="url(#Shadow)"/><use xl:href="#id8_Graphic" filter="url(#Shadow)"/><use xl:href="#id9_Graphic" filter="url(#Shadow)"/><use xl:href="#id10_Graphic" filter="url(#Shadow)"/><use xl:href="#id11_Graphic" filter="url(#Shadow)"/><use xl:href="#id12_Graphic" filter="url(#Shadow)"/><use xl:href="#id33_Graphic" filter="url(#Shadow)"/></g><g id="id6_Graphic"><path d="M 77 144 L 499 144 C 501.76142 144 504 146.23858 504 149 L 504 166 C 504 168.76142 501.76142 171 499 171 L 77 171 C 74.238576 171 72 168.76142 72 166 C 72 166 72 166 72 166 L 72 149 C 72 146.23858 74.238576 144 77 144 C 77 144 77 144 77 144 Z" fill="url(#Obj_Gradient)"/><path d="M 77 144 L 499 144 C 501.76142 144 504 146.23858 504 149 L 504 166 C 504 168.76142 501.76142 171 499 171 L 77 171 C 74.238576 171 72 168.76142 72 166 C 72 166 72 166 72 166 L 72 149 C 72 146.23858 74.238576 144 77 144 C 77 144 77 144 77 144 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 150.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" fill="black" x="153.390625" y="11" textLength="115.21875">CommonMiddleware</tspan></text></g><g id="id7_Graphic"><path d="M 77 189 L 499 189 C 501.76142 189 504 191.23858 504 194 L 504 211 C 504 213.76142 501.76142 216 499 216 L 77 216 C 74.238576 216 72 213.76142 72 211 C 72 211 72 211 72 211 L 72 194 C 72 191.23858 74.238576 189 77 189 C 77 189 77 189 77 189 Z" fill="url(#Obj_Gradient_2)"/><path d="M 77 189 L 499 189 C 501.76142 189 504 191.23858 504 194 L 504 211 C 504 213.76142 501.76142 216 499 216 L 77 216 C 74.238576 216 72 213.76142 72 211 C 72 211 72 211 72 211 L 72 194 C 72 191.23858 74.238576 189 77 189 C 77 189 77 189 77 189 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 195.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="149.79004" y="11" textLength="122.41992">SessionMiddleware</tspan></text></g><g id="id8_Graphic"><path d="M 77 234 L 499 234 C 501.76142 234 504 236.23858 504 239 L 504 256 C 504 258.76142 501.76142 261 499 261 L 77 261 C 74.238576 261 72 258.76142 72 256 C 72 256 72 256 72 256 L 72 239 C 72 236.23858 74.238576 234 77 234 C 77 234 77 234 77 234 Z" fill="url(#Obj_Gradient_3)"/><path d="M 77 234 L 499 234 C 501.76142 234 504 236.23858 504 239 L 504 256 C 504 258.76142 501.76142 261 499 261 L 77 261 C 74.238576 261 72 258.76142 72 256 C 72 256 72 256 72 256 L 72 239 C 72 236.23858 74.238576 234 77 234 C 77 234 77 234 77 234 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 240.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="146.18945" y="11" textLength="129.62109">CsrfViewMiddleware</tspan></text></g><g id="id9_Graphic"><path d="M 77 279 L 499 279 C 501.76142 279 504 281.23858 504 284 L 504 301 C 504 303.76142 501.76142 306 499 306 L 77 306 C 74.238576 306 72 303.76142 72 301 C 72 301 72 301 72 301 L 72 284 C 72 281.23858 74.238576 279 77 279 C 77 279 77 279 77 279 Z" fill="url(#Obj_Gradient_4)"/><path d="M 77 279 L 499 279 C 501.76142 279 504 281.23858 504 284 L 504 301 C 504 303.76142 501.76142 306 499 306 L 77 306 C 74.238576 306 72 303.76142 72 301 C 72 301 72 301 72 301 L 72 284 C 72 281.23858 74.238576 279 77 279 C 77 279 77 279 77 279 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 285.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="124.58594" y="11" textLength="172.82812">AuthenticationMiddleware</tspan></text></g><g id="id10_Graphic"><path d="M 77 324 L 499 324 C 501.76142 324 504 326.23858 504 329 L 504 346 C 504 348.76142 501.76142 351 499 351 L 77 351 C 74.238576 351 72 348.76142 72 346 C 72 346 72 346 72 346 L 72 329 C 72 326.23858 74.238576 324 77 324 C 77 324 77 324 77 324 Z" fill="url(#Obj_Gradient_5)"/><path d="M 77 324 L 499 324 C 501.76142 324 504 326.23858 504 329 L 504 346 C 504 348.76142 501.76142 351 499 351 L 77 351 C 74.238576 351 72 348.76142 72 346 C 72 346 72 346 72 346 L 72 329 C 72 326.23858 74.238576 324 77 324 C 77 324 77 324 77 324 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 330.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="149.79004" y="11" textLength="122.41992">MessageMiddleware</tspan></text></g><g id="id11_Graphic"><rect x="72" y="63" width="144" height="27" fill="white"/><rect x="72" y="63" width="144" height="27" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 69.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="27.393555" y="11" textLength="79.21289">HttpRequest</tspan></text></g><g id="id12_Graphic"><rect x="360" y="63" width="144" height="27" fill="white"/><rect x="360" y="63" width="144" height="27" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(365 69.5)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="23.792969" y="11" textLength="86.41406">HttpResponse</tspan></text></g><path d="M 99 90 L 117 90 L 117 360 L 126 360 L 108 387 L 90 360 L 99 360 Z" fill="white" fill-opacity=".8"/><path d="M 99 90 L 117 90 L 117 360 L 126 360 L 108 387 L 90 360 L 99 360 Z" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(115 132.125) rotate(90)" fill="green"><tspan font-family="Courier" font-size="12" font-weight="500" fill="green" x="52.36621" y="11" textLength="108.01758">process_request</tspan></text><path d="M 162 117 L 180 117 L 180 378 L 189 378 L 171 405 L 153 378 L 162 378 Z" fill="white" fill-opacity=".8"/><path d="M 162 117 L 180 117 L 180 378 L 189 378 L 171 405 L 153 378 L 162 378 Z" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(178 158) rotate(90)" fill="green"><tspan font-family="Courier" font-size="12" font-weight="500" fill="green" x="59.79297" y="11" textLength="86.41406">process_view</tspan></text><path d="M 441 405 L 423 405 L 423 135 L 414 135 L 432 108 L 450 135 L 441 135 Z" fill="white" fill-opacity=".8"/><path d="M 441 405 L 423 405 L 423 135 L 414 135 L 432 108 L 450 135 L 441 135 Z" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(425 362.875) rotate(-90)" fill="green"><tspan font-family="Courier" font-size="12" font-weight="500" fill="green" x="16.360352" y="11" textLength="180.0293">process_template_response</tspan></text><path d="M 495 405 L 477 405 L 477 117 L 468 117 L 486 90 L 504 117 L 495 117 Z" fill="white" fill-opacity=".8"/><path d="M 495 405 L 477 405 L 477 117 L 468 117 L 486 90 L 504 117 L 495 117 Z" stroke="green" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(479 360.625) rotate(-90)" fill="green"><tspan font-family="Courier" font-size="12" font-weight="500" fill="green" x="55.515625" y="11" textLength="115.21875">process_response</tspan></text><path d="M 387 405 L 369 405 L 369 135 L 360 135 L 378 108 L 396 135 L 387 135 Z" fill="white" fill-opacity=".8"/><path d="M 387 405 L 369 405 L 369 135 L 360 135 L 378 108 L 396 135 L 387 135 Z" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(371 362.875) rotate(-90)" fill="red"><tspan font-family="Courier" font-size="12" font-weight="500" fill="red" x="45.16504" y="11" textLength="122.41992">process_exception</tspan></text><g id="id33_Graphic"><path d="M 144 405 L 505.4694 405 L 513 418.5 L 505.4694 432 L 144 432 L 153 418.5 Z" fill="white"/><path d="M 144 405 L 505.4694 405 L 513 418.5 L 505.4694 432 L 144 432 L 153 418.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(149 411.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="black" x="144.81543" y="11" textLength="69.36914">view function</tspan></text></g></g></g></svg> diff --git a/docs/topics/http/middleware.txt b/docs/topics/http/middleware.txt index 16fdace825..1f5414e95b 100644 --- a/docs/topics/http/middleware.txt +++ b/docs/topics/http/middleware.txt @@ -16,18 +16,128 @@ how to write your own middleware. Django ships with some built-in middleware you can use right out of the box. They're documented in the :doc:`built-in middleware reference </ref/middleware>`. +.. versionchanged:: 1.10 + + A new style of middleware was introduced for use with the new + :setting:`MIDDLEWARE` setting. If you're using the old + :setting:`MIDDLEWARE_CLASSES` setting, you'll need to :ref:`adapt old, + custom middleware <upgrading-middleware>` before using the new setting. + This document describes new-style middleware. Refer to this page in older + versions of the documentation for a description of how old-style middleware + works. + +Writing your own middleware +=========================== + +A middleware factory is a callable that takes a ``get_response`` callable and +returns a middleware. A middleware is a callable that takes a request and +returns a response, just like a view. + +A middleware can be written as a function that looks like this:: + + def simple_middleware(get_response): + # One-time configuration and initialization. + + def middleware(request): + # Code to be executed for each request before + # the view is called. + + try: + response = get_response(request) + except Exception as e: + # Code to handle an exception that wasn't caught + # further up the chain, if desired. + ... + + # Code to be executed for each request/response after + # the view is called. + + return response + + return middleware + +Or it can be written as a class with a ``__call__()`` method, like this:: + + class SimpleMiddleware(object): + def __init__(self, get_response): + self.get_response = get_response + # One-time configuration and initialization. + + def __call__(self, request): + # Code to be executed for each request before + # the view is called. + + try: + response = self.get_response(request) + except Exception as e: + # Code to handle an exception that wasn't caught + # further up the chain, if desired. + ... + + # Code to be executed for each request/response after + # the view is called. + + return response + +In both examples, the ``try``/``except`` isn't required if the middleware +doesn't need to handle any exceptions. If it is included, it should probably +catch something more specific than ``Exception``. + +The ``get_response`` callable provided by Django might be the actual view (if +this is the last listed middleware) or it might be the next middleware in the +chain. The current middleware doesn't need to know or care what exactly it is, +just that it represents whatever comes next. + +The above is a slight simplification -- the ``get_response`` callable for the +last middleware in the chain won't be the actual view but rather a wrapper +method from the handler which takes care of applying :ref:`view middleware +<view-middleware>`, calling the view with appropriate URL arguments, and +applying :ref:`template-response <template-response-middleware>` middleware. + +Middleware can live anywhere on your Python path. + +``__init__(get_response)`` +-------------------------- + +Middleware classes must accept a ``get_response`` argument. You can also +initialize some global state for the middleware. Keep in mind a couple of +caveats: + +* Django initializes your middleware with only the ``get_response`` argument, + so you can't define ``__init__()`` as requiring any other arguments. + +* Unlike the ``__call__()`` method which get called once per request, + ``__init__()`` is called only *once*, when the Web server starts. + +.. versionchanged:: 1.10 + + In older versions, ``__init__`` was not called until the Web server + responded to its first request. + + If you want to allow your middleware to be used in Django 1.9 and earlier, + make ``get_response`` an optional argument (``get_response=None``). + +Marking middleware as unused +---------------------------- + +It's sometimes useful to determine at startup time whether a piece of +middleware should be used. In these cases, your middleware's ``__init__()`` +method may raise :exc:`~django.core.exceptions.MiddlewareNotUsed`. Django will +then remove that middleware from the middleware process and log a debug message +to the :ref:`django-request-logger` logger when :setting:`DEBUG` is ``True``. + Activating middleware ===================== -To activate a middleware component, add it to the -:setting:`MIDDLEWARE_CLASSES` list in your Django settings. +To activate a middleware component, add it to the :setting:`MIDDLEWARE` list in +your Django settings. -In :setting:`MIDDLEWARE_CLASSES`, each middleware component is represented by -a string: the full Python path to the middleware's class name. For example, +In :setting:`MIDDLEWARE`, each middleware component is represented by a string: +the full Python path to the middleware's class or function name. For example, here's the default value created by :djadmin:`django-admin startproject <startproject>`:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -37,13 +147,12 @@ here's the default value created by :djadmin:`django-admin startproject 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] -A Django installation doesn't require any middleware — -:setting:`MIDDLEWARE_CLASSES` can be empty, if you'd like — but it's strongly -suggested that you at least use +A Django installation doesn't require any middleware — :setting:`MIDDLEWARE` +can be empty, if you'd like — but it's strongly suggested that you at least use :class:`~django.middleware.common.CommonMiddleware`. -The order in :setting:`MIDDLEWARE_CLASSES` matters because a middleware can -depend on other middleware. For instance, +The order in :setting:`MIDDLEWARE` matters because a middleware can depend on +other middleware. For instance, :class:`~django.contrib.auth.middleware.AuthenticationMiddleware` stores the authenticated user in the session; therefore, it must run after :class:`~django.contrib.sessions.middleware.SessionMiddleware`. See @@ -54,55 +163,21 @@ Hooks and application order =========================== During the request phase, before calling the view, Django applies middleware -in the order it's defined in :setting:`MIDDLEWARE_CLASSES`, top-down. Two -hooks are available: - -* :meth:`process_request` -* :meth:`process_view` - -During the response phase, after calling the view, middleware are applied in -reverse order, from the bottom up. Three hooks are available: - -* :meth:`process_exception` (only if the view raised an exception) -* :meth:`process_template_response` (only for template responses) -* :meth:`process_response` +in the order it's defined in :setting:`MIDDLEWARE`, top-down. You can think of +it like an onion: each middleware class is a "layer" that wraps the view. -.. image:: _images/middleware.* - :alt: middleware application order - :width: 481 - :height: 409 +Middleware see only the changes made by middleware that run before it. A +middleware (and the view) is skipped entirely if a preceding middleware +short-circuits by returning a response without ever calling ``get_response``. +That response will only pass through the middleware that have already run. -If you prefer, you can also think of it like an onion: each middleware class -is a "layer" that wraps the view. - -The behavior of each hook is described below. - -Writing your own middleware -=========================== +Similarly, a middleware that sees the request on the way in and doesn't return +a response is guaranteed that it will always see the response on the way back +out. If the middleware also wants to see any uncaught exception on the way out, +it can wrap its call to ``get_response()`` in a ``try``/``except``. -Writing your own middleware is easy. Each middleware component is a single -Python class that defines one or more of the following methods: - -.. _request-middleware: - -``process_request()`` ---------------------- - -.. method:: process_request(request) - -``request`` is an :class:`~django.http.HttpRequest` object. - -``process_request()`` is called on each request, before Django decides which -view to execute. - -It should return either ``None`` or an :class:`~django.http.HttpResponse` -object. If it returns ``None``, Django will continue processing this request, -executing any other ``process_request()`` middleware, then, ``process_view()`` -middleware, and finally, the appropriate view. If it returns an -:class:`~django.http.HttpResponse` object, Django won't bother calling any -other request, view or exception middleware, or the appropriate view; it'll -apply response middleware to that :class:`~django.http.HttpResponse`, and -return the result. +Besides the middleware pattern described earlier, you can add two other methods +to class-based middleware: .. _view-middleware: @@ -125,14 +200,13 @@ It should return either ``None`` or an :class:`~django.http.HttpResponse` object. If it returns ``None``, Django will continue processing this request, executing any other ``process_view()`` middleware and, then, the appropriate view. If it returns an :class:`~django.http.HttpResponse` object, Django won't -bother calling any other view or exception middleware, or the appropriate -view; it'll apply response middleware to that -:class:`~django.http.HttpResponse`, and return the result. +bother calling the appropriate view; it'll apply response middleware to that +:class:`~django.http.HttpResponse` and return the result. .. note:: Accessing :attr:`request.POST <django.http.HttpRequest.POST>` inside - middleware from ``process_request`` or ``process_view`` will prevent any + middleware before the view runs or in ``process_view()`` will prevent any view running after the middleware from being able to :ref:`modify the upload handlers for the request <modifying_upload_handlers_on_the_fly>`, and should normally be avoided. @@ -170,41 +244,8 @@ called. Middleware are run in reverse order during the response phase, which includes ``process_template_response()``. -.. _response-middleware: - -``process_response()`` ----------------------- - -.. method:: process_response(request, response) - -``request`` is an :class:`~django.http.HttpRequest` object. ``response`` is -the :class:`~django.http.HttpResponse` or -:class:`~django.http.StreamingHttpResponse` object returned by a Django view -or by a middleware. - -``process_response()`` is called on all responses before they're returned to -the browser. - -It must return an :class:`~django.http.HttpResponse` or -:class:`~django.http.StreamingHttpResponse` object. It could alter the given -``response``, or it could create and return a brand-new -:class:`~django.http.HttpResponse` or -:class:`~django.http.StreamingHttpResponse`. - -Unlike the ``process_request()`` and ``process_view()`` methods, the -``process_response()`` method is always called, even if the -``process_request()`` and ``process_view()`` methods of the same middleware -class were skipped (because an earlier middleware method returned an -:class:`~django.http.HttpResponse`). In particular, this means that your -``process_response()`` method cannot rely on setup done in -``process_request()``. - -Finally, remember that during the response phase, middleware are applied in -reverse order, from the bottom up. This means classes defined at the end of -:setting:`MIDDLEWARE_CLASSES` will be run first. - Dealing with streaming responses -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +================================ Unlike :class:`~django.http.HttpResponse`, :class:`~django.http.StreamingHttpResponse` does not have a ``content`` @@ -229,66 +270,62 @@ must test for streaming responses and adjust their behavior accordingly:: .. _exception-middleware: -``process_exception()`` ------------------------ +Exception middleware +==================== -.. method:: process_exception(request, exception) +A middleware that does some custom exception handling might looks like this:: -``request`` is an :class:`~django.http.HttpRequest` object. ``exception`` is an -``Exception`` object raised by the view function. + class ExceptionMiddleware(object): + def __init__(self, get_response): + self.get_response = get_response -Django calls ``process_exception()`` when a view raises an exception. -``process_exception()`` should return either ``None`` or an -:class:`~django.http.HttpResponse` object. If it returns an -:class:`~django.http.HttpResponse` object, the template response and response -middleware will be applied, and the resulting response returned to the -browser. Otherwise, default exception handling kicks in. + def __call__(self, request): + try: + response = self.get_response(request) + except Exception as e: + # Do something with the exception and possibly reraise it + # unless you wish to silence it. + ... + return response -Again, middleware are run in reverse order during the response phase, which -includes ``process_exception``. If an exception middleware returns a response, -the middleware classes above that middleware will not be called at all. +Middleware that wants to do something for all exception responses, an HTTP 404 +for example, need to both catch the appropriate exception (e.g. ``Http404``) +and look for regular responses with the status code of interest. You can +subclass :class:`~django.middleware.exception.ExceptionMiddleware` if you want +to transform exceptions into the appropriate response. -``__init__()`` --------------- +.. _upgrading-middleware: -Most middleware classes won't need an initializer since middleware classes are -essentially placeholders for the ``process_*`` methods. If you do need some -global state you may use ``__init__`` to set up. However, keep in mind a couple -of caveats: +Upgrading pre-Django 1.10-style middleware +========================================== -* Django initializes your middleware without any arguments, so you can't - define ``__init__`` as requiring any arguments. - -* Unlike the ``process_*`` methods which get called once per request, - ``__init__`` gets called only *once*, when the Web server starts. - -.. versionchanged:: 1.10 - - In older versions, ``__init__`` was not called until the Web server - responded to its first request. - -Marking middleware as unused -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. class:: django.utils.deprecation.MiddlewareMixin + :module: -It's sometimes useful to determine at run-time whether a piece of middleware -should be used. In these cases, your middleware's ``__init__`` method may -raise :exc:`django.core.exceptions.MiddlewareNotUsed`. Django will then remove -that piece of middleware from the middleware process and a debug message will -be logged to the ``django.request`` logger when :setting:`DEBUG` is set to -``True``. +Django provides ``django.utils.deprecation.MiddlewareMixin`` to ease providing +the existing built-in middleware in both new-style and old-style forms and to +ease similar conversions of third-party middleware. -Guidelines ----------- +In most cases, this mixin will be sufficient to convert a middleware with +sufficient backwards-compatibility; the new short-circuiting semantics will be +harmless or even beneficial to the existing middleware. -* Middleware classes don't have to subclass anything. +In a few cases, a middleware class may need more invasive changes to adjust to +the new semantics. -* The middleware class can live anywhere on your Python path. All Django - cares about is that the :setting:`MIDDLEWARE_CLASSES` setting includes - the path to it. +For example, in the current request-handling logic, the handler transforms any +exception that passes through all ``process_exception`` middleware uncaught +into a response with appropriate status code (e.g. 404, 403, 400, or 500), and +then passes that response through the full chain of ``process_response`` +middleware. -* Feel free to look at :doc:`Django's available middleware - </ref/middleware>` for examples. +In new-style middleware, a given middleware only gets one shot at a given +response or uncaught exception "on the way out," and will see either a returned +response or an uncaught exception, but not both. -* If you write a middleware component that you think would be useful to - other people, contribute to the community! :doc:`Let us know - </internals/contributing/index>`, and we'll consider adding it to Django. +This means that certain middleware which want to do something with all 404 +responses (for example, the ``RedirectFallbackMiddleware`` and +``FlatpageFallbackMiddleware`` in ``django.contrib.redirects`` and +``django.contrib.flatpages``) now need to watch out for both a 404 response +and an uncaught ``Http404`` exception. They do this by subclassing +:class:`~django.middleware.exception.ExceptionMiddleware`. diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt index 34a1b28b73..1a948397ae 100644 --- a/docs/topics/http/sessions.txt +++ b/docs/topics/http/sessions.txt @@ -18,13 +18,13 @@ Sessions are implemented via a piece of :doc:`middleware </ref/middleware>`. To enable session functionality, do the following: -* Edit the :setting:`MIDDLEWARE_CLASSES` setting and make sure - it contains ``'django.contrib.sessions.middleware.SessionMiddleware'``. - The default ``settings.py`` created by ``django-admin startproject`` - has ``SessionMiddleware`` activated. +* Edit the :setting:`MIDDLEWARE` setting and make sure it contains + ``'django.contrib.sessions.middleware.SessionMiddleware'``. The default + ``settings.py`` created by ``django-admin startproject`` has + ``SessionMiddleware`` activated. If you don't want to use sessions, you might as well remove the -``SessionMiddleware`` line from :setting:`MIDDLEWARE_CLASSES` and +``SessionMiddleware`` line from :setting:`MIDDLEWARE` and ``'django.contrib.sessions'`` from your :setting:`INSTALLED_APPS`. It'll save you a small bit of overhead. diff --git a/docs/topics/http/urls.txt b/docs/topics/http/urls.txt index 81dc4a0d94..ae5b714726 100644 --- a/docs/topics/http/urls.txt +++ b/docs/topics/http/urls.txt @@ -41,8 +41,8 @@ algorithm the system follows to determine which Python code to execute: 1. Django determines the root URLconf module to use. Ordinarily, this is the value of the :setting:`ROOT_URLCONF` setting, but if the incoming ``HttpRequest`` object has a :attr:`~django.http.HttpRequest.urlconf` - attribute (set by middleware :ref:`request processing <request-middleware>`), - its value will be used in place of the :setting:`ROOT_URLCONF` setting. + attribute (set by middleware), its value will be used in place of the + :setting:`ROOT_URLCONF` setting. 2. Django loads that Python module and looks for the variable ``urlpatterns``. This should be a Python list of :func:`django.conf.urls.url` diff --git a/docs/topics/i18n/timezones.txt b/docs/topics/i18n/timezones.txt index 8ea63f2908..674edc1a19 100644 --- a/docs/topics/i18n/timezones.txt +++ b/docs/topics/i18n/timezones.txt @@ -175,13 +175,14 @@ the most likely choices. Here's an example that stores the current timezone in the session. (It skips error handling entirely for the sake of simplicity.) -Add the following middleware to :setting:`MIDDLEWARE_CLASSES`:: +Add the following middleware to :setting:`MIDDLEWARE`:: import pytz from django.utils import timezone + from django.django.utils.deprecation import MiddlewareMixin - class TimezoneMiddleware(object): + class TimezoneMiddleware(MiddlewareMixin): def process_request(self, request): tzname = request.session.get('django_timezone') if tzname: diff --git a/docs/topics/i18n/translation.txt b/docs/topics/i18n/translation.txt index 43184a13a1..3f13021bc8 100644 --- a/docs/topics/i18n/translation.txt +++ b/docs/topics/i18n/translation.txt @@ -38,7 +38,7 @@ make some optimizations so as not to load the internationalization machinery. .. note:: Make sure you've activated translation for your project (the fastest way is - to check if :setting:`MIDDLEWARE_CLASSES` includes + to check if :setting:`MIDDLEWARE` includes :mod:`django.middleware.locale.LocaleMiddleware`). If you haven't yet, see :ref:`how-django-discovers-language-preference`. @@ -1422,7 +1422,7 @@ Django provides two mechanisms to internationalize URL patterns: Using either one of these features requires that an active language be set for each request; in other words, you need to have :class:`django.middleware.locale.LocaleMiddleware` in your - :setting:`MIDDLEWARE_CLASSES` setting. + :setting:`MIDDLEWARE` setting. Language prefix in URL patterns ------------------------------- @@ -2065,8 +2065,8 @@ prefer, then you also need to use the ``LocaleMiddleware``. It customizes content for each user. To use ``LocaleMiddleware``, add ``'django.middleware.locale.LocaleMiddleware'`` -to your :setting:`MIDDLEWARE_CLASSES` setting. Because middleware order -matters, you should follow these guidelines: +to your :setting:`MIDDLEWARE` setting. Because middleware order matters, follow +these guidelines: * Make sure it's one of the first middlewares installed. * It should come after ``SessionMiddleware``, because ``LocaleMiddleware`` @@ -2075,9 +2075,9 @@ matters, you should follow these guidelines: to resolve the requested URL. * If you use ``CacheMiddleware``, put ``LocaleMiddleware`` after it. -For example, your :setting:`MIDDLEWARE_CLASSES` might look like this:: +For example, your :setting:`MIDDLEWARE` might look like this:: - MIDDLEWARE_CLASSES = [ + MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 8db06c15a3..d2ef214c36 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1182,7 +1182,7 @@ easy:: class MiddlewareTestCase(TestCase): def test_cache_middleware(self): - with self.modify_settings(MIDDLEWARE_CLASSES={ + with self.modify_settings(MIDDLEWARE={ 'append': 'django.middleware.cache.FetchFromCacheMiddleware', 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', 'remove': [ @@ -1233,7 +1233,7 @@ decorator:: class MiddlewareTestCase(TestCase): - @modify_settings(MIDDLEWARE_CLASSES={ + @modify_settings(MIDDLEWARE={ 'append': 'django.middleware.cache.FetchFromCacheMiddleware', 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', }) @@ -1245,7 +1245,7 @@ The decorator can also be applied to test case classes:: from django.test import TestCase, modify_settings - @modify_settings(MIDDLEWARE_CLASSES={ + @modify_settings(MIDDLEWARE={ 'append': 'django.middleware.cache.FetchFromCacheMiddleware', 'prepend': 'django.middleware.cache.UpdateCacheMiddleware', }) diff --git a/tests/auth_tests/settings.py b/tests/auth_tests/settings.py index 7da6144d4b..8c295387c4 100644 --- a/tests/auth_tests/settings.py +++ b/tests/auth_tests/settings.py @@ -2,7 +2,7 @@ import os from django.utils._os import upath -AUTH_MIDDLEWARE_CLASSES = [ +AUTH_MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ] diff --git a/tests/auth_tests/test_context_processors.py b/tests/auth_tests/test_context_processors.py index 3a8d800c74..d1db63c7b2 100644 --- a/tests/auth_tests/test_context_processors.py +++ b/tests/auth_tests/test_context_processors.py @@ -5,7 +5,7 @@ from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.test import SimpleTestCase, TestCase, override_settings -from .settings import AUTH_MIDDLEWARE_CLASSES, AUTH_TEMPLATES +from .settings import AUTH_MIDDLEWARE, AUTH_TEMPLATES class MockUser(object): @@ -67,7 +67,7 @@ class AuthContextProcessorTests(TestCase): def setUpTestData(cls): cls.superuser = User.objects.create_superuser(username='super', password='secret', email='super@example.com') - @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE_CLASSES) + @override_settings(MIDDLEWARE=AUTH_MIDDLEWARE) def test_session_not_accessed(self): """ Tests that the session is not accessed simply by including @@ -76,7 +76,12 @@ class AuthContextProcessorTests(TestCase): response = self.client.get('/auth_processor_no_attr_access/') self.assertContains(response, "Session not accessed") - @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE_CLASSES) + @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE, MIDDLEWARE=None) + def test_session_not_accessed_middleware_classes(self): + response = self.client.get('/auth_processor_no_attr_access/') + self.assertContains(response, "Session not accessed") + + @override_settings(MIDDLEWARE=AUTH_MIDDLEWARE) def test_session_is_accessed(self): """ Tests that the session is accessed if the auth context processor @@ -85,6 +90,11 @@ class AuthContextProcessorTests(TestCase): response = self.client.get('/auth_processor_attr_access/') self.assertContains(response, "Session accessed") + @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE, MIDDLEWARE=None) + def test_session_is_accessed_middleware_classes(self): + response = self.client.get('/auth_processor_attr_access/') + self.assertContains(response, "Session accessed") + def test_perms_attrs(self): u = User.objects.create_user(username='normal', password='secret') u.user_permissions.add( diff --git a/tests/auth_tests/test_remote_user.py b/tests/auth_tests/test_remote_user.py index 3d77ea3b89..1cb7666a49 100644 --- a/tests/auth_tests/test_remote_user.py +++ b/tests/auth_tests/test_remote_user.py @@ -23,7 +23,7 @@ class RemoteUserTest(TestCase): def setUp(self): self.patched_settings = modify_settings( AUTHENTICATION_BACKENDS={'append': self.backend}, - MIDDLEWARE_CLASSES={'append': self.middleware}, + MIDDLEWARE={'append': self.middleware}, ) self.patched_settings.enable() @@ -151,6 +151,21 @@ class RemoteUserTest(TestCase): self.assertTrue(response.context['user'].is_anonymous) +@override_settings(MIDDLEWARE=None) +class RemoteUserTestMiddlewareClasses(RemoteUserTest): + + def setUp(self): + self.patched_settings = modify_settings( + AUTHENTICATION_BACKENDS={'append': self.backend}, + MIDDLEWARE_CLASSES={'append': [ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + self.middleware, + ]}, + ) + self.patched_settings.enable() + + class RemoteUserNoCreateBackend(RemoteUserBackend): """Backend that doesn't create unknown users.""" create_unknown_user = False diff --git a/tests/check_framework/test_security.py b/tests/check_framework/test_security.py index 34254b66d5..b4591030f5 100644 --- a/tests/check_framework/test_security.py +++ b/tests/check_framework/test_security.py @@ -1,5 +1,6 @@ from django.conf import settings from django.core.checks.security import base, csrf, sessions +from django.core.checks.utils import patch_middleware_message from django.test import SimpleTestCase from django.test.utils import override_settings @@ -13,7 +14,7 @@ class CheckSessionCookieSecureTest(SimpleTestCase): @override_settings( SESSION_COOKIE_SECURE=False, INSTALLED_APPS=["django.contrib.sessions"], - MIDDLEWARE_CLASSES=[]) + MIDDLEWARE=[]) def test_session_cookie_secure_with_installed_app(self): """ Warn if SESSION_COOKIE_SECURE is off and "django.contrib.sessions" is @@ -23,20 +24,36 @@ class CheckSessionCookieSecureTest(SimpleTestCase): @override_settings( SESSION_COOKIE_SECURE=False, + INSTALLED_APPS=["django.contrib.sessions"], + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[]) + def test_session_cookie_secure_with_installed_app_middleware_classes(self): + self.assertEqual(self.func(None), [sessions.W010]) + + @override_settings( + SESSION_COOKIE_SECURE=False, INSTALLED_APPS=[], - MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_with_middleware(self): """ Warn if SESSION_COOKIE_SECURE is off and "django.contrib.sessions.middleware.SessionMiddleware" is in - MIDDLEWARE_CLASSES. + MIDDLEWARE. """ self.assertEqual(self.func(None), [sessions.W011]) @override_settings( SESSION_COOKIE_SECURE=False, - INSTALLED_APPS=["django.contrib.sessions"], + INSTALLED_APPS=[], + MIDDLEWARE=None, MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + def test_session_cookie_secure_with_middleware_middleware_classes(self): + self.assertEqual(self.func(None), [patch_middleware_message(sessions.W011)]) + + @override_settings( + SESSION_COOKIE_SECURE=False, + INSTALLED_APPS=["django.contrib.sessions"], + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_both(self): """ If SESSION_COOKIE_SECURE is off and we find both the session app and @@ -45,9 +62,17 @@ class CheckSessionCookieSecureTest(SimpleTestCase): self.assertEqual(self.func(None), [sessions.W012]) @override_settings( - SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_SECURE=False, INSTALLED_APPS=["django.contrib.sessions"], + MIDDLEWARE=None, MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + def test_session_cookie_secure_both_middleware_classes(self): + self.assertEqual(self.func(None), [sessions.W012]) + + @override_settings( + SESSION_COOKIE_SECURE=True, + INSTALLED_APPS=["django.contrib.sessions"], + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_true(self): """ If SESSION_COOKIE_SECURE is on, there's no warning about it. @@ -64,7 +89,7 @@ class CheckSessionCookieHttpOnlyTest(SimpleTestCase): @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=["django.contrib.sessions"], - MIDDLEWARE_CLASSES=[]) + MIDDLEWARE=[]) def test_session_cookie_httponly_with_installed_app(self): """ Warn if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions" @@ -75,19 +100,19 @@ class CheckSessionCookieHttpOnlyTest(SimpleTestCase): @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=[], - MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_with_middleware(self): """ Warn if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions.middleware.SessionMiddleware" is in - MIDDLEWARE_CLASSES. + MIDDLEWARE. """ self.assertEqual(self.func(None), [sessions.W014]) @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=["django.contrib.sessions"], - MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_both(self): """ If SESSION_COOKIE_HTTPONLY is off and we find both the session app and @@ -98,7 +123,7 @@ class CheckSessionCookieHttpOnlyTest(SimpleTestCase): @override_settings( SESSION_COOKIE_HTTPONLY=True, INSTALLED_APPS=["django.contrib.sessions"], - MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) + MIDDLEWARE=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_true(self): """ If SESSION_COOKIE_HTTPONLY is on, there's no warning about it. @@ -112,15 +137,15 @@ class CheckCSRFMiddlewareTest(SimpleTestCase): from django.core.checks.security.csrf import check_csrf_middleware return check_csrf_middleware - @override_settings(MIDDLEWARE_CLASSES=[]) + @override_settings(MIDDLEWARE=[], MIDDLEWARE_CLASSES=[]) def test_no_csrf_middleware(self): """ - Warn if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES. + Warn if CsrfViewMiddleware isn't in MIDDLEWARE. """ self.assertEqual(self.func(None), [csrf.W003]) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"]) + MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"]) def test_with_csrf_middleware(self): self.assertEqual(self.func(None), []) @@ -132,25 +157,25 @@ class CheckCSRFCookieSecureTest(SimpleTestCase): return check_csrf_cookie_secure @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], + MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_SECURE=False) def test_with_csrf_cookie_secure_false(self): """ - Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but + Warn if CsrfViewMiddleware is in MIDDLEWARE but CSRF_COOKIE_SECURE isn't True. """ self.assertEqual(self.func(None), [csrf.W016]) - @override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_SECURE=False) + @override_settings(MIDDLEWARE=[], MIDDLEWARE_CLASSES=[], CSRF_COOKIE_SECURE=False) def test_with_csrf_cookie_secure_false_no_middleware(self): """ - No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if + No warning if CsrfViewMiddleware isn't in MIDDLEWARE, even if CSRF_COOKIE_SECURE is False. """ self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], + MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_SECURE=True) def test_with_csrf_cookie_secure_true(self): self.assertEqual(self.func(None), []) @@ -163,25 +188,25 @@ class CheckCSRFCookieHttpOnlyTest(SimpleTestCase): return check_csrf_cookie_httponly @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], + MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_HTTPONLY=False) def test_with_csrf_cookie_httponly_false(self): """ - Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but + Warn if CsrfViewMiddleware is in MIDDLEWARE but CSRF_COOKIE_HTTPONLY isn't True. """ self.assertEqual(self.func(None), [csrf.W017]) - @override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_HTTPONLY=False) + @override_settings(MIDDLEWARE=[], MIDDLEWARE_CLASSES=[], CSRF_COOKIE_HTTPONLY=False) def test_with_csrf_cookie_httponly_false_no_middleware(self): """ - No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if + No warning if CsrfViewMiddleware isn't in MIDDLEWARE, even if CSRF_COOKIE_HTTPONLY is False. """ self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], + MIDDLEWARE=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_HTTPONLY=True) def test_with_csrf_cookie_httponly_true(self): self.assertEqual(self.func(None), []) @@ -193,15 +218,15 @@ class CheckSecurityMiddlewareTest(SimpleTestCase): from django.core.checks.security.base import check_security_middleware return check_security_middleware - @override_settings(MIDDLEWARE_CLASSES=[]) + @override_settings(MIDDLEWARE=[]) def test_no_security_middleware(self): """ - Warn if SecurityMiddleware isn't in MIDDLEWARE_CLASSES. + Warn if SecurityMiddleware isn't in MIDDLEWARE. """ self.assertEqual(self.func(None), [base.W001]) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"]) + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"]) def test_with_security_middleware(self): self.assertEqual(self.func(None), []) @@ -213,7 +238,7 @@ class CheckStrictTransportSecurityTest(SimpleTestCase): return check_sts @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_SECONDS=0) def test_no_sts(self): """ @@ -222,7 +247,7 @@ class CheckStrictTransportSecurityTest(SimpleTestCase): self.assertEqual(self.func(None), [base.W004]) @override_settings( - MIDDLEWARE_CLASSES=[], + MIDDLEWARE=[], SECURE_HSTS_SECONDS=0) def test_no_sts_no_middleware(self): """ @@ -232,7 +257,7 @@ class CheckStrictTransportSecurityTest(SimpleTestCase): self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_SECONDS=3600) def test_with_sts(self): self.assertEqual(self.func(None), []) @@ -245,7 +270,7 @@ class CheckStrictTransportSecuritySubdomainsTest(SimpleTestCase): return check_sts_include_subdomains @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_INCLUDE_SUBDOMAINS=False, SECURE_HSTS_SECONDS=3600) def test_no_sts_subdomains(self): @@ -255,7 +280,7 @@ class CheckStrictTransportSecuritySubdomainsTest(SimpleTestCase): self.assertEqual(self.func(None), [base.W005]) @override_settings( - MIDDLEWARE_CLASSES=[], + MIDDLEWARE=[], SECURE_HSTS_INCLUDE_SUBDOMAINS=False, SECURE_HSTS_SECONDS=3600) def test_no_sts_subdomains_no_middleware(self): @@ -265,7 +290,7 @@ class CheckStrictTransportSecuritySubdomainsTest(SimpleTestCase): self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=False, SECURE_HSTS_SECONDS=None) def test_no_sts_subdomains_no_seconds(self): @@ -275,7 +300,7 @@ class CheckStrictTransportSecuritySubdomainsTest(SimpleTestCase): self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_SECONDS=3600) def test_with_sts_subdomains(self): @@ -288,14 +313,14 @@ class CheckXFrameOptionsMiddlewareTest(SimpleTestCase): from django.core.checks.security.base import check_xframe_options_middleware return check_xframe_options_middleware - @override_settings(MIDDLEWARE_CLASSES=[]) + @override_settings(MIDDLEWARE=[]) def test_middleware_not_installed(self): """ - Warn if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES. + Warn if XFrameOptionsMiddleware isn't in MIDDLEWARE. """ self.assertEqual(self.func(None), [base.W002]) - @override_settings(MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"]) + @override_settings(MIDDLEWARE=["django.middleware.clickjacking.XFrameOptionsMiddleware"]) def test_middleware_installed(self): self.assertEqual(self.func(None), []) @@ -307,26 +332,26 @@ class CheckXFrameOptionsDenyTest(SimpleTestCase): return check_xframe_deny @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"], + MIDDLEWARE=["django.middleware.clickjacking.XFrameOptionsMiddleware"], X_FRAME_OPTIONS='SAMEORIGIN', ) def test_x_frame_options_not_deny(self): """ - Warn if XFrameOptionsMiddleware is in MIDDLEWARE_CLASSES but + Warn if XFrameOptionsMiddleware is in MIDDLEWARE but X_FRAME_OPTIONS isn't 'DENY'. """ self.assertEqual(self.func(None), [base.W019]) - @override_settings(MIDDLEWARE_CLASSES=[], X_FRAME_OPTIONS='SAMEORIGIN') + @override_settings(MIDDLEWARE=[], X_FRAME_OPTIONS='SAMEORIGIN') def test_middleware_not_installed(self): """ - No error if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES even if + No error if XFrameOptionsMiddleware isn't in MIDDLEWARE even if X_FRAME_OPTIONS isn't 'DENY'. """ self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"], + MIDDLEWARE=["django.middleware.clickjacking.XFrameOptionsMiddleware"], X_FRAME_OPTIONS='DENY', ) def test_xframe_deny(self): @@ -340,7 +365,7 @@ class CheckContentTypeNosniffTest(SimpleTestCase): return check_content_type_nosniff @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_CONTENT_TYPE_NOSNIFF=False) def test_no_content_type_nosniff(self): """ @@ -349,17 +374,17 @@ class CheckContentTypeNosniffTest(SimpleTestCase): self.assertEqual(self.func(None), [base.W006]) @override_settings( - MIDDLEWARE_CLASSES=[], + MIDDLEWARE=[], SECURE_CONTENT_TYPE_NOSNIFF=False) def test_no_content_type_nosniff_no_middleware(self): """ Don't warn if SECURE_CONTENT_TYPE_NOSNIFF isn't True and - SecurityMiddleware isn't in MIDDLEWARE_CLASSES. + SecurityMiddleware isn't in MIDDLEWARE. """ self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_CONTENT_TYPE_NOSNIFF=True) def test_with_content_type_nosniff(self): self.assertEqual(self.func(None), []) @@ -372,7 +397,7 @@ class CheckXssFilterTest(SimpleTestCase): return check_xss_filter @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_BROWSER_XSS_FILTER=False) def test_no_xss_filter(self): """ @@ -381,17 +406,17 @@ class CheckXssFilterTest(SimpleTestCase): self.assertEqual(self.func(None), [base.W007]) @override_settings( - MIDDLEWARE_CLASSES=[], + MIDDLEWARE=[], SECURE_BROWSER_XSS_FILTER=False) def test_no_xss_filter_no_middleware(self): """ Don't warn if SECURE_BROWSER_XSS_FILTER isn't True and - SecurityMiddleware isn't in MIDDLEWARE_CLASSES. + SecurityMiddleware isn't in MIDDLEWARE. """ self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_BROWSER_XSS_FILTER=True) def test_with_xss_filter(self): self.assertEqual(self.func(None), []) @@ -404,7 +429,7 @@ class CheckSSLRedirectTest(SimpleTestCase): return check_ssl_redirect @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=False) def test_no_ssl_redirect(self): """ @@ -413,7 +438,7 @@ class CheckSSLRedirectTest(SimpleTestCase): self.assertEqual(self.func(None), [base.W008]) @override_settings( - MIDDLEWARE_CLASSES=[], + MIDDLEWARE=[], SECURE_SSL_REDIRECT=False) def test_no_ssl_redirect_no_middleware(self): """ @@ -423,7 +448,7 @@ class CheckSSLRedirectTest(SimpleTestCase): self.assertEqual(self.func(None), []) @override_settings( - MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], + MIDDLEWARE=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=True) def test_with_ssl_redirect(self): self.assertEqual(self.func(None), []) diff --git a/tests/file_uploads/tests.py b/tests/file_uploads/tests.py index 924b13bec1..2712cf668a 100644 --- a/tests/file_uploads/tests.py +++ b/tests/file_uploads/tests.py @@ -27,7 +27,7 @@ MEDIA_ROOT = sys_tempfile.mkdtemp() UPLOAD_TO = os.path.join(MEDIA_ROOT, 'test_upload') -@override_settings(MEDIA_ROOT=MEDIA_ROOT, ROOT_URLCONF='file_uploads.urls', MIDDLEWARE_CLASSES=[]) +@override_settings(MEDIA_ROOT=MEDIA_ROOT, ROOT_URLCONF='file_uploads.urls', MIDDLEWARE=[]) class FileUploadTests(TestCase): @classmethod diff --git a/tests/flatpages_tests/test_csrf.py b/tests/flatpages_tests/test_csrf.py index 0ffc567c48..b4ae186d0c 100644 --- a/tests/flatpages_tests/test_csrf.py +++ b/tests/flatpages_tests/test_csrf.py @@ -9,7 +9,7 @@ from .settings import FLATPAGES_TEMPLATES @modify_settings(INSTALLED_APPS={'append': 'django.contrib.flatpages'}) @override_settings( LOGIN_URL='/accounts/login/', - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -97,3 +97,18 @@ class FlatpageCSRFTests(TestCase): "POSTing to an unknown page isn't caught as a 403 CSRF error" response = self.client.post('/no_such_page/') self.assertEqual(response.status_code, 404) + + +@override_settings( + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + ], +) +class FlatpageCSRFMiddlewareClassesTests(FlatpageCSRFTests): + pass diff --git a/tests/flatpages_tests/test_forms.py b/tests/flatpages_tests/test_forms.py index ae9da29f5c..acf6687ea2 100644 --- a/tests/flatpages_tests/test_forms.py +++ b/tests/flatpages_tests/test_forms.py @@ -47,18 +47,36 @@ class FlatpageAdminFormTests(TestCase): self.assertFalse(form.is_valid()) self.assertEqual(form.errors['url'], ["URL is missing a leading slash."]) - @override_settings(APPEND_SLASH=True, MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware']) + @override_settings(APPEND_SLASH=True, MIDDLEWARE=['django.middleware.common.CommonMiddleware']) def test_flatpage_requires_trailing_slash_with_append_slash(self): form = FlatpageForm(data=dict(url='/no_trailing_slash', **self.form_data)) with translation.override('en'): self.assertFalse(form.is_valid()) self.assertEqual(form.errors['url'], ["URL is missing a trailing slash."]) - @override_settings(APPEND_SLASH=False, MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware']) + @override_settings(APPEND_SLASH=False, MIDDLEWARE=['django.middleware.common.CommonMiddleware']) def test_flatpage_doesnt_requires_trailing_slash_without_append_slash(self): form = FlatpageForm(data=dict(url='/no_trailing_slash', **self.form_data)) self.assertTrue(form.is_valid()) + @override_settings( + APPEND_SLASH=True, MIDDLEWARE=None, + MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware'], + ) + def test_flatpage_requires_trailing_slash_with_append_slash_middleware_classes(self): + form = FlatpageForm(data=dict(url='/no_trailing_slash', **self.form_data)) + with translation.override('en'): + self.assertFalse(form.is_valid()) + self.assertEqual(form.errors['url'], ["URL is missing a trailing slash."]) + + @override_settings( + APPEND_SLASH=False, MIDDLEWARE=None, + MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware'], + ) + def test_flatpage_doesnt_requires_trailing_slash_without_append_slash_middleware_classes(self): + form = FlatpageForm(data=dict(url='/no_trailing_slash', **self.form_data)) + self.assertTrue(form.is_valid()) + def test_flatpage_admin_form_url_uniqueness_validation(self): "The flatpage admin form correctly enforces url uniqueness among flatpages of the same site" data = dict(url='/myflatpage1/', **self.form_data) diff --git a/tests/flatpages_tests/test_middleware.py b/tests/flatpages_tests/test_middleware.py index 37e789d1b1..8d04076af6 100644 --- a/tests/flatpages_tests/test_middleware.py +++ b/tests/flatpages_tests/test_middleware.py @@ -40,7 +40,7 @@ class TestDataMixin(object): @modify_settings(INSTALLED_APPS={'append': 'django.contrib.flatpages'}) @override_settings( LOGIN_URL='/accounts/login/', - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -107,11 +107,26 @@ class FlatpageMiddlewareTests(TestDataMixin, TestCase): self.assertContains(response, "<p>Isn't it special!</p>") +@override_settings( + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + ], +) +class FlatpageMiddlewareClassesTests(FlatpageMiddlewareTests): + pass + + @modify_settings(INSTALLED_APPS={'append': 'django.contrib.flatpages'}) @override_settings( APPEND_SLASH=True, LOGIN_URL='/accounts/login/', - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -172,3 +187,18 @@ class FlatpageMiddlewareAppendSlashTests(TestDataMixin, TestCase): response = self.client.get('/') self.assertContains(response, "<p>Root</p>") + + +@override_settings( + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', + ], +) +class FlatpageAppendSlashMiddlewareClassesTests(FlatpageMiddlewareAppendSlashTests): + pass diff --git a/tests/flatpages_tests/test_templatetags.py b/tests/flatpages_tests/test_templatetags.py index d766d13964..688d85a224 100644 --- a/tests/flatpages_tests/test_templatetags.py +++ b/tests/flatpages_tests/test_templatetags.py @@ -2,25 +2,9 @@ from django.contrib.auth.models import AnonymousUser, User from django.contrib.flatpages.models import FlatPage from django.contrib.sites.models import Site from django.template import Context, Template, TemplateSyntaxError -from django.test import TestCase, modify_settings, override_settings +from django.test import TestCase -from .settings import FLATPAGES_TEMPLATES - -@modify_settings(INSTALLED_APPS={'append': 'django.contrib.flatpages'}) -@override_settings( - MIDDLEWARE_CLASSES=[ - 'django.middleware.common.CommonMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', - ], - ROOT_URLCONF='flatpages_tests.urls', - TEMPLATES=FLATPAGES_TEMPLATES, - SITE_ID=1, -) class FlatpageTemplateTagTests(TestCase): @classmethod diff --git a/tests/flatpages_tests/test_views.py b/tests/flatpages_tests/test_views.py index 1a93a543b7..a5104ce620 100644 --- a/tests/flatpages_tests/test_views.py +++ b/tests/flatpages_tests/test_views.py @@ -40,7 +40,7 @@ class TestDataMixin(object): @modify_settings(INSTALLED_APPS={'append': 'django.contrib.flatpages'}) @override_settings( LOGIN_URL='/accounts/login/', - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -102,7 +102,7 @@ class FlatpageViewTests(TestDataMixin, TestCase): @override_settings( APPEND_SLASH=True, LOGIN_URL='/accounts/login/', - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py index d0b161cf44..9f01cb201a 100644 --- a/tests/handlers/tests.py +++ b/tests/handlers/tests.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import unittest +from django.core.exceptions import ImproperlyConfigured from django.core.handlers.wsgi import WSGIHandler, WSGIRequest, get_script_name from django.core.signals import request_finished, request_started from django.db import close_old_connections, connection @@ -166,6 +167,10 @@ class SignalsTests(SimpleTestCase): self.assertEqual(self.signals, ['started', 'finished']) +def empty_middleware(get_response): + pass + + @override_settings(ROOT_URLCONF='handlers.urls') class HandlerRequestTests(SimpleTestCase): @@ -199,6 +204,12 @@ class HandlerRequestTests(SimpleTestCase): WSGIHandler()(environ, start_response) self.assertEqual(start_response.status, '200 OK') + @override_settings(MIDDLEWARE=['handlers.tests.empty_middleware']) + def test_middleware_returns_none(self): + msg = 'Middleware factory handlers.tests.empty_middleware returned None.' + with self.assertRaisesMessage(ImproperlyConfigured, msg): + self.client.get('/') + class ScriptNameTests(SimpleTestCase): def test_get_script_name(self): diff --git a/tests/i18n/patterns/tests.py b/tests/i18n/patterns/tests.py index c503a74d34..613a88701f 100644 --- a/tests/i18n/patterns/tests.py +++ b/tests/i18n/patterns/tests.py @@ -30,7 +30,7 @@ class PermanentRedirectLocaleMiddleWare(LocaleMiddleware): ('en', 'English'), ('pt-br', 'Brazilian Portuguese'), ], - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ], @@ -223,7 +223,7 @@ class URLRedirectTests(URLTestCaseBase): self.assertEqual(response.status_code, 200) @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'i18n.patterns.tests.PermanentRedirectLocaleMiddleWare', 'django.middleware.common.CommonMiddleware', ], diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 140e2da187..5084f2dc1d 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -1756,7 +1756,7 @@ class MultipleLocaleActivationTests(SimpleTestCase): ('en', 'English'), ('fr', 'French'), ], - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ], @@ -1772,7 +1772,7 @@ class LocaleMiddlewareTests(TestCase): self.assertContains(response, "Yes/No") @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', @@ -1792,7 +1792,7 @@ class LocaleMiddlewareTests(TestCase): ('en', 'English'), ('fr', 'French'), ], - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ], @@ -1828,7 +1828,7 @@ class UnprefixedDefaultLanguageTests(SimpleTestCase): ('en-us', 'English'), ('pt-br', 'Portuguese (Brazil)'), ], - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ], diff --git a/tests/logging_tests/tests.py b/tests/logging_tests/tests.py index 058abfb4a3..1802a0ce47 100644 --- a/tests/logging_tests/tests.py +++ b/tests/logging_tests/tests.py @@ -125,7 +125,7 @@ class HandlerLoggingTests(SetupDefaultLoggingMixin, LoggingCaptureMixin, SimpleT DEBUG=True, USE_I18N=True, LANGUAGES=[('en', 'English')], - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', ], diff --git a/tests/messages_tests/base.py b/tests/messages_tests/base.py index 3284522edf..7b6cd43a1d 100644 --- a/tests/messages_tests/base.py +++ b/tests/messages_tests/base.py @@ -217,7 +217,7 @@ class BaseTests(object): @modify_settings( INSTALLED_APPS={'remove': 'django.contrib.messages'}, - MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'}, + MIDDLEWARE={'remove': 'django.contrib.messages.middleware.MessageMiddleware'}, ) @override_settings( MESSAGE_LEVEL=constants.DEBUG, @@ -243,7 +243,7 @@ class BaseTests(object): @modify_settings( INSTALLED_APPS={'remove': 'django.contrib.messages'}, - MIDDLEWARE_CLASSES={'remove': 'django.contrib.messages.middleware.MessageMiddleware'}, + MIDDLEWARE={'remove': 'django.contrib.messages.middleware.MessageMiddleware'}, ) @override_settings( TEMPLATES=[{ diff --git a/tests/middleware_exceptions/middleware.py b/tests/middleware_exceptions/middleware.py index 944dd8910d..6871d9556e 100644 --- a/tests/middleware_exceptions/middleware.py +++ b/tests/middleware_exceptions/middleware.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals from django.http import HttpResponse +from django.utils.deprecation import MiddlewareMixin -class ProcessExceptionMiddleware(object): +class ProcessExceptionMiddleware(MiddlewareMixin): + def process_exception(self, request, exception): return HttpResponse('Exception caught') diff --git a/tests/middleware_exceptions/tests.py b/tests/middleware_exceptions/tests.py index ef9d19fe21..13b33632f8 100644 --- a/tests/middleware_exceptions/tests.py +++ b/tests/middleware_exceptions/tests.py @@ -8,6 +8,7 @@ from django.template import engines from django.template.response import TemplateResponse from django.test import RequestFactory, SimpleTestCase, override_settings from django.test.utils import patch_logger +from django.utils.deprecation import MiddlewareMixin class TestException(Exception): @@ -15,13 +16,14 @@ class TestException(Exception): # A middleware base class that tracks which methods have been called -class TestMiddleware(object): - def __init__(self): +class TestMiddleware(MiddlewareMixin): + def __init__(self, get_response=None): self.process_request_called = False self.process_view_called = False self.process_response_called = False self.process_template_response_called = False self.process_exception_called = False + self.get_response = get_response def process_request(self, request): self.process_request_called = True @@ -115,7 +117,11 @@ class NoResponseMiddleware(TestMiddleware): super(NoResponseMiddleware, self).process_response(request, response) -@override_settings(ROOT_URLCONF='middleware_exceptions.urls') +@override_settings( + ROOT_URLCONF='middleware_exceptions.urls', + MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware'], + MIDDLEWARE=None, +) class BaseMiddlewareExceptionTest(SimpleTestCase): def setUp(self): @@ -492,12 +498,10 @@ class MiddlewareTests(BaseMiddlewareExceptionTest): # Check that the right middleware methods have been invoked self.assert_middleware_usage(middleware, True, True, True, True, False) - @override_settings( - MIDDLEWARE_CLASSES=['middleware_exceptions.middleware.ProcessExceptionMiddleware'], - ) + @override_settings(MIDDLEWARE=['middleware_exceptions.middleware.ProcessExceptionMiddleware']) def test_exception_in_render_passed_to_process_exception(self): # Repopulate the list of middlewares since it's already been populated - # by setUp() before the MIDDLEWARE_CLASSES setting got overridden + # by setUp() before the MIDDLEWARE setting got overridden. self.client.handler.load_middleware() response = self.client.get('/middleware_exceptions/exception_in_render/') self.assertEqual(response.content, b'Exception caught') @@ -868,7 +872,7 @@ class RootUrlconfTests(SimpleTestCase): class MyMiddleware(object): - def __init__(self): + def __init__(self, get_response=None): raise MiddlewareNotUsed def process_request(self, request): @@ -877,7 +881,7 @@ class MyMiddleware(object): class MyMiddlewareWithExceptionMessage(object): - def __init__(self): + def __init__(self, get_response=None): raise MiddlewareNotUsed('spam eggs') def process_request(self, request): @@ -887,6 +891,7 @@ class MyMiddlewareWithExceptionMessage(object): @override_settings( DEBUG=True, ROOT_URLCONF='middleware_exceptions.urls', + MIDDLEWARE=['django.middleware.common.CommonMiddleware'], ) class MiddlewareNotUsedTests(SimpleTestCase): @@ -897,9 +902,7 @@ class MiddlewareNotUsedTests(SimpleTestCase): with self.assertRaises(MiddlewareNotUsed): MyMiddleware().process_request(request) - @override_settings(MIDDLEWARE_CLASSES=[ - 'middleware_exceptions.tests.MyMiddleware', - ]) + @override_settings(MIDDLEWARE=['middleware_exceptions.tests.MyMiddleware']) def test_log(self): with patch_logger('django.request', 'debug') as calls: self.client.get('/middleware_exceptions/view/') @@ -909,9 +912,7 @@ class MiddlewareNotUsedTests(SimpleTestCase): "MiddlewareNotUsed: 'middleware_exceptions.tests.MyMiddleware'" ) - @override_settings(MIDDLEWARE_CLASSES=[ - 'middleware_exceptions.tests.MyMiddlewareWithExceptionMessage', - ]) + @override_settings(MIDDLEWARE=['middleware_exceptions.tests.MyMiddlewareWithExceptionMessage']) def test_log_custom_message(self): with patch_logger('django.request', 'debug') as calls: self.client.get('/middleware_exceptions/view/') @@ -926,3 +927,11 @@ class MiddlewareNotUsedTests(SimpleTestCase): with patch_logger('django.request', 'debug') as calls: self.client.get('/middleware_exceptions/view/') self.assertEqual(len(calls), 0) + + +@override_settings( + MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware'], + MIDDLEWARE=None, +) +class MiddlewareNotUsedMiddlewareClassesTests(MiddlewareNotUsedTests): + pass diff --git a/tests/project_template/test_settings.py b/tests/project_template/test_settings.py index 702ecec8c5..d153c4d95f 100644 --- a/tests/project_template/test_settings.py +++ b/tests/project_template/test_settings.py @@ -26,16 +26,16 @@ class TestStartProjectSettings(TestCase): shutil.copyfile(template_settings_py, test_settings_py) self.addCleanup(os.remove, test_settings_py) - def test_middleware_classes_headers(self): + def test_middleware_headers(self): """ - Ensure headers sent by the default MIDDLEWARE_CLASSES do not - inadvertently change. For example, we never want "Vary: Cookie" to - appear in the list since it prevents the caching of responses. + Ensure headers sent by the default MIDDLEWARE don't inadvertently + change. For example, we never want "Vary: Cookie" to appear in the list + since it prevents the caching of responses. """ - from django.conf.project_template.project_name.settings import MIDDLEWARE_CLASSES + from django.conf.project_template.project_name.settings import MIDDLEWARE with self.settings( - MIDDLEWARE_CLASSES=MIDDLEWARE_CLASSES, + MIDDLEWARE=MIDDLEWARE, ROOT_URLCONF='project_template.urls', ): response = self.client.get('/empty/') diff --git a/tests/redirects_tests/tests.py b/tests/redirects_tests/tests.py index 2d9793b0a5..b4210ad49b 100644 --- a/tests/redirects_tests/tests.py +++ b/tests/redirects_tests/tests.py @@ -8,7 +8,7 @@ from django.test import TestCase, modify_settings, override_settings from django.utils import six -@modify_settings(MIDDLEWARE_CLASSES={'append': 'django.contrib.redirects.middleware.RedirectFallbackMiddleware'}) +@modify_settings(MIDDLEWARE={'append': 'django.contrib.redirects.middleware.RedirectFallbackMiddleware'}) @override_settings(APPEND_SLASH=False, SITE_ID=1) class RedirectTests(TestCase): @@ -42,6 +42,18 @@ class RedirectTests(TestCase): response = self.client.get('/initial') self.assertEqual(response.status_code, 410) + @override_settings(MIDDLEWARE=None) + @modify_settings(MIDDLEWARE_CLASSES={'append': 'django.contrib.redirects.middleware.RedirectFallbackMiddleware'}) + def test_redirect_middleware_classes(self): + self.test_redirect() + + @override_settings(MIDDLEWARE=None) + @modify_settings(MIDDLEWARE_CLASSES={'append': 'django.contrib.redirects.middleware.RedirectFallbackMiddleware'}) + def test_more_redirects_middleware_classes(self): + self.test_redirect_with_append_slash() + self.test_redirect_with_append_slash_and_query_string() + self.test_response_gone() + @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) def test_sites_not_installed(self): with self.assertRaises(ImproperlyConfigured): @@ -54,7 +66,7 @@ class OverriddenRedirectFallbackMiddleware(RedirectFallbackMiddleware): response_redirect_class = http.HttpResponseRedirect -@modify_settings(MIDDLEWARE_CLASSES={'append': 'redirects_tests.tests.OverriddenRedirectFallbackMiddleware'}) +@modify_settings(MIDDLEWARE={'append': 'redirects_tests.tests.OverriddenRedirectFallbackMiddleware'}) @override_settings(SITE_ID=1) class OverriddenRedirectMiddlewareTests(TestCase): diff --git a/tests/runtests.py b/tests/runtests.py index aedd4aa932..122cb73282 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -62,7 +62,7 @@ ALWAYS_INSTALLED_APPS = [ 'django.contrib.staticfiles', ] -ALWAYS_MIDDLEWARE_CLASSES = [ +ALWAYS_MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -124,7 +124,7 @@ def setup(verbosity, test_labels, parallel): 'LANGUAGE_CODE': settings.LANGUAGE_CODE, 'STATIC_URL': settings.STATIC_URL, 'STATIC_ROOT': settings.STATIC_ROOT, - 'MIDDLEWARE_CLASSES': settings.MIDDLEWARE_CLASSES, + 'MIDDLEWARE': settings.MIDDLEWARE, } # Redirect some settings for the duration of these tests. @@ -147,7 +147,7 @@ def setup(verbosity, test_labels, parallel): }] settings.LANGUAGE_CODE = 'en' settings.SITE_ID = 1 - settings.MIDDLEWARE_CLASSES = ALWAYS_MIDDLEWARE_CLASSES + settings.MIDDLEWARE = ALWAYS_MIDDLEWARE settings.MIGRATION_MODULES = { # This lets us skip creating migrations for the test models as many of # them depend on one of the following contrib applications. diff --git a/tests/template_tests/test_response.py b/tests/template_tests/test_response.py index 85b5f4bf0f..c2ece4b2a8 100644 --- a/tests/template_tests/test_response.py +++ b/tests/template_tests/test_response.py @@ -4,13 +4,15 @@ import pickle import time from datetime import datetime -from django.conf import settings from django.template import engines from django.template.response import ( ContentNotRenderedError, SimpleTemplateResponse, TemplateResponse, ) -from django.test import RequestFactory, SimpleTestCase, override_settings +from django.test import ( + RequestFactory, SimpleTestCase, modify_settings, override_settings, +) from django.test.utils import require_jinja2 +from django.utils.deprecation import MiddlewareMixin from .utils import TEMPLATE_DIR @@ -21,7 +23,7 @@ test_processor_name = 'template_tests.test_response.test_processor' # A test middleware that installs a temporary URLConf -class CustomURLConfMiddleware(object): +class CustomURLConfMiddleware(MiddlewareMixin): def process_request(self, request): request.urlconf = 'template_tests.alternate_urls' @@ -319,12 +321,8 @@ class TemplateResponseTest(SimpleTestCase): pickle.dumps(unpickled_response) -@override_settings( - MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [ - 'template_tests.test_response.CustomURLConfMiddleware' - ], - ROOT_URLCONF='template_tests.urls', -) +@modify_settings(MIDDLEWARE={'append': ['template_tests.test_response.CustomURLConfMiddleware']}) +@override_settings(ROOT_URLCONF='template_tests.urls') class CustomURLConfTest(SimpleTestCase): def test_custom_urlconf(self): @@ -332,16 +330,47 @@ class CustomURLConfTest(SimpleTestCase): self.assertContains(response, 'This is where you can find the snark: /snark/') +@modify_settings( + MIDDLEWARE={ + 'append': [ + 'django.middleware.cache.FetchFromCacheMiddleware', + 'django.middleware.cache.UpdateCacheMiddleware', + ], + }, +) +@override_settings(CACHE_MIDDLEWARE_SECONDS=2.0, ROOT_URLCONF='template_tests.alternate_urls') +class CacheMiddlewareTest(SimpleTestCase): + + def test_middleware_caching(self): + response = self.client.get('/template_response_view/') + self.assertEqual(response.status_code, 200) + + time.sleep(1.0) + + response2 = self.client.get('/template_response_view/') + self.assertEqual(response2.status_code, 200) + + self.assertEqual(response.content, response2.content) + + time.sleep(2.0) + + # Let the cache expire and test again + response2 = self.client.get('/template_response_view/') + self.assertEqual(response2.status_code, 200) + + self.assertNotEqual(response.content, response2.content) + + @override_settings( - CACHE_MIDDLEWARE_SECONDS=2.0, - MIDDLEWARE_CLASSES=settings.MIDDLEWARE_CLASSES + [ + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ 'django.middleware.cache.FetchFromCacheMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', ], - ROOT_URLCONF='template_tests.alternate_urls', + CACHE_MIDDLEWARE_SECONDS=2.0, + ROOT_URLCONF='template_tests.alternate_urls' ) -class CacheMiddlewareTest(SimpleTestCase): - +class CacheMiddlewareClassesTest(SimpleTestCase): def test_middleware_caching(self): response = self.client.get('/template_response_view/') self.assertEqual(response.status_code, 200) diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index dff7e39080..5236846ae6 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -694,7 +694,7 @@ class ClientTest(TestCase): @override_settings( - MIDDLEWARE_CLASSES=['django.middleware.csrf.CsrfViewMiddleware'], + MIDDLEWARE=['django.middleware.csrf.CsrfViewMiddleware'], ROOT_URLCONF='test_client.urls', ) class CSRFEnabledClientTests(SimpleTestCase): diff --git a/tests/urlpatterns_reverse/middleware.py b/tests/urlpatterns_reverse/middleware.py index 13c3d104b6..8c40125f10 100644 --- a/tests/urlpatterns_reverse/middleware.py +++ b/tests/urlpatterns_reverse/middleware.py @@ -1,37 +1,38 @@ from django.http import HttpResponse, StreamingHttpResponse from django.urls import reverse +from django.utils.deprecation import MiddlewareMixin from . import urlconf_inner -class ChangeURLconfMiddleware(object): +class ChangeURLconfMiddleware(MiddlewareMixin): def process_request(self, request): request.urlconf = urlconf_inner.__name__ -class NullChangeURLconfMiddleware(object): +class NullChangeURLconfMiddleware(MiddlewareMixin): def process_request(self, request): request.urlconf = None -class ReverseInnerInResponseMiddleware(object): +class ReverseInnerInResponseMiddleware(MiddlewareMixin): def process_response(self, *args, **kwargs): return HttpResponse(reverse('inner')) -class ReverseOuterInResponseMiddleware(object): +class ReverseOuterInResponseMiddleware(MiddlewareMixin): def process_response(self, *args, **kwargs): return HttpResponse(reverse('outer')) -class ReverseInnerInStreaming(object): +class ReverseInnerInStreaming(MiddlewareMixin): def process_view(self, *args, **kwargs): def stream(): yield reverse('inner') return StreamingHttpResponse(stream()) -class ReverseOuterInStreaming(object): +class ReverseOuterInStreaming(MiddlewareMixin): def process_view(self, *args, **kwargs): def stream(): yield reverse('outer') diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index 4c60ecd7c6..074a573cd5 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -785,7 +785,7 @@ class RequestURLconfTests(SimpleTestCase): self.assertEqual(response.status_code, 404) @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.ChangeURLconfMiddleware' % middleware.__name__, ] ) @@ -799,7 +799,7 @@ class RequestURLconfTests(SimpleTestCase): self.assertEqual(response.content, b'outer:,inner:/second_test/') @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.NullChangeURLconfMiddleware' % middleware.__name__, ] ) @@ -817,7 +817,7 @@ class RequestURLconfTests(SimpleTestCase): self.assertEqual(response.status_code, 404) @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.ChangeURLconfMiddleware' % middleware.__name__, '%s.ReverseInnerInResponseMiddleware' % middleware.__name__, ] @@ -832,7 +832,7 @@ class RequestURLconfTests(SimpleTestCase): self.assertEqual(response.content, b'/second_test/') @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.ChangeURLconfMiddleware' % middleware.__name__, '%s.ReverseOuterInResponseMiddleware' % middleware.__name__, ] @@ -847,7 +847,7 @@ class RequestURLconfTests(SimpleTestCase): self.client.get('/second_test/') @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.ChangeURLconfMiddleware' % middleware.__name__, '%s.ReverseInnerInStreaming' % middleware.__name__, ] @@ -862,7 +862,7 @@ class RequestURLconfTests(SimpleTestCase): self.assertEqual(b''.join(response), b'/second_test/') @override_settings( - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ '%s.ChangeURLconfMiddleware' % middleware.__name__, '%s.ReverseOuterInStreaming' % middleware.__name__, ] diff --git a/tests/view_tests/tests/test_csrf.py b/tests/view_tests/tests/test_csrf.py index 4b21e59034..22bf96529f 100644 --- a/tests/view_tests/tests/test_csrf.py +++ b/tests/view_tests/tests/test_csrf.py @@ -15,7 +15,7 @@ class CsrfViewTests(SimpleTestCase): @override_settings( USE_I18N=True, - MIDDLEWARE_CLASSES=[ + MIDDLEWARE=[ 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -39,6 +39,32 @@ class CsrfViewTests(SimpleTestCase): status_code=403) @override_settings( + USE_I18N=True, + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ + 'django.middleware.locale.LocaleMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + ], + ) + def test_translation_middleware_classes(self): + """ + Test that an invalid request is rejected with a localized error message. + """ + response = self.client.post('/') + self.assertContains(response, "Forbidden", status_code=403) + self.assertContains(response, + "CSRF verification failed. Request aborted.", + status_code=403) + + with self.settings(LANGUAGE_CODE='nl'), override('en-us'): + response = self.client.post('/') + self.assertContains(response, "Verboden", status_code=403) + self.assertContains(response, + "CSRF-verificatie mislukt. Verzoek afgebroken.", + status_code=403) + + @override_settings( SECURE_PROXY_SSL_HEADER=('HTTP_X_FORWARDED_PROTO', 'https') ) def test_no_referer(self): diff --git a/tests/view_tests/tests/test_i18n.py b/tests/view_tests/tests/test_i18n.py index 84a381909d..a50b7d6460 100644 --- a/tests/view_tests/tests/test_i18n.py +++ b/tests/view_tests/tests/test_i18n.py @@ -123,6 +123,26 @@ class I18NTests(TestCase): # we force saving language to a cookie rather than a session # by excluding session middleware and those which do require it test_settings = dict( + MIDDLEWARE=['django.middleware.common.CommonMiddleware'], + LANGUAGE_COOKIE_NAME='mylanguage', + LANGUAGE_COOKIE_AGE=3600 * 7 * 2, + LANGUAGE_COOKIE_DOMAIN='.example.com', + LANGUAGE_COOKIE_PATH='/test/', + ) + with self.settings(**test_settings): + post_data = dict(language='pl', next='/views/') + response = self.client.post('/i18n/setlang/', data=post_data) + language_cookie = response.cookies.get('mylanguage') + self.assertEqual(language_cookie.value, 'pl') + self.assertEqual(language_cookie['domain'], '.example.com') + self.assertEqual(language_cookie['path'], '/test/') + self.assertEqual(language_cookie['max-age'], 3600 * 7 * 2) + + def test_setlang_cookie_middleware_classes(self): + # we force saving language to a cookie rather than a session + # by excluding session middleware and those which do require it + test_settings = dict( + MIDDLEWARE=None, MIDDLEWARE_CLASSES=['django.middleware.common.CommonMiddleware'], LANGUAGE_COOKIE_NAME='mylanguage', LANGUAGE_COOKIE_AGE=3600 * 7 * 2, @@ -150,7 +170,7 @@ class I18NTests(TestCase): self.assertRedirects(response, encoded_url, fetch_redirect_response=False) self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], lang_code) - @modify_settings(MIDDLEWARE_CLASSES={ + @modify_settings(MIDDLEWARE={ 'append': 'django.middleware.locale.LocaleMiddleware', }) def test_lang_from_translated_i18n_pattern(self): @@ -167,6 +187,27 @@ class I18NTests(TestCase): ) self.assertRedirects(response, '/en/translated/') + @override_settings( + MIDDLEWARE=None, + MIDDLEWARE_CLASSES=[ + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.locale.LocaleMiddleware', + ], + ) + def test_lang_from_translated_i18n_pattern_middleware_classes(self): + response = self.client.post( + '/i18n/setlang/', data={'language': 'nl'}, + follow=True, HTTP_REFERER='/en/translated/' + ) + self.assertEqual(self.client.session[LANGUAGE_SESSION_KEY], 'nl') + self.assertRedirects(response, '/nl/vertaald/') + # And reverse + response = self.client.post( + '/i18n/setlang/', data={'language': 'en'}, + follow=True, HTTP_REFERER='/nl/vertaald/' + ) + self.assertRedirects(response, '/en/translated/') + @override_settings(ROOT_URLCONF='view_tests.urls') class JsI18NTests(SimpleTestCase): |
