Index: django/middleware/common.py
===================================================================
--- django/middleware/common.py (revision 7711)
+++ django/middleware/common.py (working copy)
@@ -40,54 +40,18 @@
if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
return http.HttpResponseForbidden('
Forbidden
')
- # Check for a redirect based on settings.APPEND_SLASH
- # and settings.PREPEND_WWW
- host = request.get_host()
- old_url = [host, request.path]
- new_url = old_url[:]
+ _response = _handle_redirects(request)
+ if _response:
+ return _response
- if (settings.PREPEND_WWW and old_url[0] and
- not old_url[0].startswith('www.')):
- new_url[0] = 'www.' + old_url[0]
-
- # Append a slash if APPEND_SLASH is set and the URL doesn't have a
- # trailing slash and there is no pattern for the current path
- if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
- try:
- urlresolvers.resolve(request.path)
- except urlresolvers.Resolver404:
- new_url[1] = new_url[1] + '/'
- if settings.DEBUG and request.method == 'POST':
- raise RuntimeError, (""
- "You called this URL via POST, but the URL doesn't end "
- "in a slash and you have APPEND_SLASH set. Django can't "
- "redirect to the slash URL while maintaining POST data. "
- "Change your form to point to %s%s (note the trailing "
- "slash), or set APPEND_SLASH=False in your Django "
- "settings.") % (new_url[0], new_url[1])
-
- if new_url != old_url:
- # Redirect if the target url exists
- try:
- urlresolvers.resolve(new_url[1])
- except urlresolvers.Resolver404:
- pass
- else:
- if new_url[0]:
- newurl = "%s://%s%s" % (
- request.is_secure() and 'https' or 'http',
- new_url[0], urlquote(new_url[1]))
- else:
- newurl = urlquote(new_url[1])
- if request.GET:
- newurl += '?' + request.GET.urlencode()
- return http.HttpResponsePermanentRedirect(newurl)
-
return None
def process_response(self, request, response):
"Check for a flat page (for 404s) and calculate the Etag, if needed."
if response.status_code == 404:
+ _response = _handle_redirects(request, resolve=False)
+ if _response:
+ return _response
if settings.SEND_BROKEN_LINK_EMAILS:
# If the referrer was from an internal link or a non-search-engine site,
# send a note to the managers.
@@ -118,6 +82,52 @@
return response
+def _handle_redirects(request, resolve=True):
+ host = request.get_host()
+ old_url = [host, request.path]
+ new_url = old_url[:]
+
+ if (settings.PREPEND_WWW and old_url[0] and
+ not old_url[0].startswith('www.')):
+ new_url[0] = 'www.' + old_url[0]
+
+ # Append a slash if APPEND_SLASH is set and the URL doesn't have a
+ # trailing slash and there is no pattern for the current path
+ if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
+ if resolve:
+ try:
+ urlresolvers.resolve(request.path)
+ except urlresolvers.Resolver404:
+ new_url[1] = new_url[1] + '/'
+ else:
+ new_url[1] = new_url[1] + '/'
+
+ if new_url != old_url:
+ # Redirect if the target url exists
+ try:
+ if resolve and new_url[0] == new_url[0] and new_url[1] != old_url[1]:
+ urlresolvers.resolve(new_url[1])
+ except urlresolvers.Resolver404:
+ pass
+ else:
+ if new_url[0]:
+ newurl = "%s://%s%s" % (
+ request.is_secure() and 'https' or 'http',
+ new_url[0], urlquote(new_url[1]))
+ else:
+ newurl = urlquote(new_url[1])
+ if request.GET:
+ newurl += '?' + request.GET.urlencode()
+ if settings.DEBUG and request.method == 'POST':
+ raise RuntimeError, (""
+ "You called this URL via POST, but the URL doesn't end "
+ "in a slash and you have APPEND_SLASH set. Django can't "
+ "redirect to the slash URL while maintaining POST data. "
+ "Change your form to point to %s%s (note the trailing "
+ "slash), or set APPEND_SLASH=False in your Django "
+ "settings.") % (new_url[0], new_url[1])
+ return http.HttpResponsePermanentRedirect(newurl)
+
def _is_ignorable_404(uri):
"Returns True if a 404 at the given URL *shouldn't* notify the site managers"
for start in settings.IGNORABLE_404_STARTS:
Index: tests/regressiontests/middleware/tests.py
===================================================================
--- tests/regressiontests/middleware/tests.py (revision 7711)
+++ tests/regressiontests/middleware/tests.py (working copy)
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
from django.test import TestCase
-from django.http import HttpRequest
+from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
from django.middleware.common import CommonMiddleware
from django.conf import settings
@@ -89,3 +89,70 @@
self.assertEquals(
r['Location'],
'http://testserver/middleware/needsquoting%23/')
+
+ def test_append_slash_enabled_custom_middleware_slash(self):
+ """
+ Tests that if some intermediate middleware (e.g.
+ FlatpageFallbackMiddleware) has processed the request and returned a
+ response, this is not redirected.
+ """
+ settings.APPEND_SLASH = True
+ request = self._get_request('custom/slash')
+ common_middleware = CommonMiddleware()
+ custom_middleware = CustomMiddleware()
+ response = common_middleware.process_request(request)
+ if response is None:
+ response = custom_middleware.process_request(request)
+ response = custom_middleware.process_response(request, response)
+ response = common_middleware.process_response(request, response)
+ self.assertEquals(response.status_code, 301)
+ self.assertEquals(
+ response['Location'],
+ 'http://testserver/middleware/custom/slash/')
+
+ def test_append_slash_disabled_custom_middleware_slash(self):
+ """
+ Tests that if some intermediate middleware (e.g.
+ FlatpageFallbackMiddleware) has processed the request and returned a
+ response, this is not redirected.
+ """
+ settings.APPEND_SLASH = False
+ request = self._get_request('custom/slash')
+ common_middleware = CommonMiddleware()
+ custom_middleware = CustomMiddleware()
+ response = common_middleware.process_request(request)
+ self.assertEquals(response, None)
+ response = custom_middleware.process_request(request)
+ self.assertEquals(response.status_code, 404)
+ response = custom_middleware.process_response(request, response)
+ self.assertEquals(response.status_code, 404)
+ response = common_middleware.process_response(request, response)
+ self.assertEquals(response.status_code, 404)
+
+ def test_append_slash_custom_middleware_slashless(self):
+ """
+ Tests that if some intermediate middleware (e.g.
+ FlatpageFallbackMiddleware) has processed the request and returned a
+ response, this is not redirected.
+ """
+ for append_slash in (True, False):
+ settings.APPEND_SLASH = append_slash
+ request = self._get_request('custom/noslash')
+ common_middleware = CommonMiddleware()
+ custom_middleware = CustomMiddleware()
+ response = common_middleware.process_request(request)
+ self.assertEquals(response, None)
+ response = custom_middleware.process_request(request)
+ self.assertContains(response, 'Custom middleware response')
+ response = custom_middleware.process_response(request, response)
+ self.assertContains(response, 'Custom middleware response')
+ response = common_middleware.process_response(request, response)
+ self.assertContains(response, 'Custom middleware response')
+
+class CustomMiddleware(object):
+ def process_request(self, request):
+ if request.path in ('/middleware/custom/slash/', '/middleware/custom/noslash'):
+ return HttpResponse('Custom middleware response')
+ return HttpResponseNotFound()
+ def process_response(self, request, response):
+ return response