Fixed #17734 -- Made sure to only redirect translated URLs if they can actually be resolved to prevent unwanted redirects. Many thanks to Orne Brocaar and Anssi Kääriäinen for input.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@17621 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
a255d39338
commit
746987f916
|
@ -518,3 +518,17 @@ def get_urlconf(default=None):
|
||||||
changed from the default one.
|
changed from the default one.
|
||||||
"""
|
"""
|
||||||
return getattr(_urlconfs, "value", default)
|
return getattr(_urlconfs, "value", default)
|
||||||
|
|
||||||
|
def is_valid_path(path, urlconf=None):
|
||||||
|
"""
|
||||||
|
Returns True if the given path resolves against the default URL resolver,
|
||||||
|
False otherwise.
|
||||||
|
|
||||||
|
This is a convenience method to make working with "is this a match?" cases
|
||||||
|
easier, avoiding unnecessarily indented try...except blocks.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
resolve(path, urlconf)
|
||||||
|
return True
|
||||||
|
except Resolver404:
|
||||||
|
return False
|
||||||
|
|
|
@ -64,8 +64,8 @@ class CommonMiddleware(object):
|
||||||
# trailing slash and there is no pattern for the current path
|
# trailing slash and there is no pattern for the current path
|
||||||
if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
|
if settings.APPEND_SLASH and (not old_url[1].endswith('/')):
|
||||||
urlconf = getattr(request, 'urlconf', None)
|
urlconf = getattr(request, 'urlconf', None)
|
||||||
if (not _is_valid_path(request.path_info, urlconf) and
|
if (not urlresolvers.is_valid_path(request.path_info, urlconf) and
|
||||||
_is_valid_path("%s/" % request.path_info, urlconf)):
|
urlresolvers.is_valid_path("%s/" % request.path_info, urlconf)):
|
||||||
new_url[1] = new_url[1] + '/'
|
new_url[1] = new_url[1] + '/'
|
||||||
if settings.DEBUG and request.method == 'POST':
|
if settings.DEBUG and request.method == 'POST':
|
||||||
raise RuntimeError((""
|
raise RuntimeError((""
|
||||||
|
@ -151,18 +151,3 @@ def _is_internal_request(domain, referer):
|
||||||
"""
|
"""
|
||||||
# Different subdomains are treated as different domains.
|
# Different subdomains are treated as different domains.
|
||||||
return referer is not None and re.match("^https?://%s/" % re.escape(domain), referer)
|
return referer is not None and re.match("^https?://%s/" % re.escape(domain), referer)
|
||||||
|
|
||||||
def _is_valid_path(path, urlconf=None):
|
|
||||||
"""
|
|
||||||
Returns True if the given path resolves against the default URL resolver,
|
|
||||||
False otherwise.
|
|
||||||
|
|
||||||
This is a convenience method to make working with "is this a match?" cases
|
|
||||||
easier, avoiding unnecessarily indented try...except blocks.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
urlresolvers.resolve(path, urlconf)
|
|
||||||
return True
|
|
||||||
except urlresolvers.Resolver404:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
"This is the locale selecting middleware that will look at accept headers"
|
"This is the locale selecting middleware that will look at accept headers"
|
||||||
|
|
||||||
from django.core.urlresolvers import get_resolver, LocaleRegexURLResolver
|
from django.conf import settings
|
||||||
|
from django.core.urlresolvers import (is_valid_path, get_resolver,
|
||||||
|
LocaleRegexURLResolver)
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.cache import patch_vary_headers
|
from django.utils.cache import patch_vary_headers
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
|
|
||||||
|
|
||||||
class LocaleMiddleware(object):
|
class LocaleMiddleware(object):
|
||||||
"""
|
"""
|
||||||
This is a very simple middleware that parses a request
|
This is a very simple middleware that parses a request
|
||||||
|
@ -23,13 +26,17 @@ class LocaleMiddleware(object):
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
language = translation.get_language()
|
language = translation.get_language()
|
||||||
translation.deactivate()
|
|
||||||
|
|
||||||
if (response.status_code == 404 and
|
if (response.status_code == 404 and
|
||||||
not translation.get_language_from_path(request.path_info)
|
not translation.get_language_from_path(request.path_info)
|
||||||
and self.is_language_prefix_patterns_used()):
|
and self.is_language_prefix_patterns_used()):
|
||||||
return HttpResponseRedirect(
|
urlconf = getattr(request, 'urlconf', None)
|
||||||
'/%s%s' % (language, request.get_full_path()))
|
language_path = '/%s%s' % (language, request.path_info)
|
||||||
|
if settings.APPEND_SLASH and not language_path.endswith('/'):
|
||||||
|
language_path = language_path + '/'
|
||||||
|
if is_valid_path(language_path, urlconf):
|
||||||
|
return HttpResponseRedirect(
|
||||||
|
'/%s%s' % (language, request.get_full_path()))
|
||||||
|
translation.deactivate()
|
||||||
|
|
||||||
patch_vary_headers(response, ('Accept-Language',))
|
patch_vary_headers(response, ('Accept-Language',))
|
||||||
if 'Content-Language' not in response:
|
if 'Content-Language' not in response:
|
||||||
|
|
|
@ -144,37 +144,29 @@ class URLRedirectTests(URLTestCaseBase):
|
||||||
|
|
||||||
def test_en_redirect(self):
|
def test_en_redirect(self):
|
||||||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertRedirects(response, 'http://testserver/en/account/register/')
|
self.assertRedirects(response, '/en/account/register/')
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
response = self.client.get(response['location'])
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_en_redirect_wrong_url(self):
|
def test_en_redirect_wrong_url(self):
|
||||||
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
self.assertEqual(response['location'], 'http://testserver/en/profiel/registeren/')
|
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
def test_nl_redirect(self):
|
def test_nl_redirect(self):
|
||||||
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='nl')
|
response = self.client.get('/profiel/registeren/', HTTP_ACCEPT_LANGUAGE='nl')
|
||||||
self.assertRedirects(response, 'http://testserver/nl/profiel/registeren/')
|
self.assertRedirects(response, '/nl/profiel/registeren/')
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
response = self.client.get(response['location'])
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
def test_nl_redirect_wrong_url(self):
|
def test_nl_redirect_wrong_url(self):
|
||||||
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='nl')
|
response = self.client.get('/account/register/', HTTP_ACCEPT_LANGUAGE='nl')
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
self.assertEqual(response['location'], 'http://testserver/nl/account/register/')
|
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
def test_pt_br_redirect(self):
|
def test_pt_br_redirect(self):
|
||||||
response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
|
response = self.client.get('/conta/registre-se/', HTTP_ACCEPT_LANGUAGE='pt-br')
|
||||||
self.assertRedirects(response, 'http://testserver/pt-br/conta/registre-se/')
|
self.assertRedirects(response, '/pt-br/conta/registre-se/')
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
response = self.client.get(response['location'])
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
@ -187,17 +179,15 @@ class URLRedirectWithoutTrailingSlashTests(URLTestCaseBase):
|
||||||
"""
|
"""
|
||||||
def test_not_prefixed_redirect(self):
|
def test_not_prefixed_redirect(self):
|
||||||
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertEqual(response.status_code, 301)
|
self.assertRedirects(response, '/not-prefixed/', 301)
|
||||||
self.assertEqual(response['location'], 'http://testserver/not-prefixed/')
|
|
||||||
|
|
||||||
def test_en_redirect(self):
|
def test_en_redirect(self):
|
||||||
response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertEqual(response.status_code, 302)
|
# target status code of 301 because of CommonMiddleware redirecting
|
||||||
self.assertEqual(response['location'], 'http://testserver/en/account/register')
|
self.assertRedirects(response, '/en/account/register', 302, target_status_code=301)
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
response = self.client.get(response['location'])
|
||||||
self.assertEqual(response.status_code, 301)
|
self.assertRedirects(response, '/en/account/register/', 301)
|
||||||
self.assertEqual(response['location'], 'http://testserver/en/account/register/')
|
|
||||||
|
|
||||||
|
|
||||||
class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
|
class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
|
||||||
|
@ -208,20 +198,15 @@ class URLRedirectWithoutTrailingSlashSettingTests(URLTestCaseBase):
|
||||||
@override_settings(APPEND_SLASH=False)
|
@override_settings(APPEND_SLASH=False)
|
||||||
def test_not_prefixed_redirect(self):
|
def test_not_prefixed_redirect(self):
|
||||||
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/not-prefixed', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertEqual(response.status_code, 302)
|
|
||||||
self.assertEqual(response['location'], 'http://testserver/en/not-prefixed')
|
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
|
|
||||||
@override_settings(APPEND_SLASH=False)
|
@override_settings(APPEND_SLASH=False)
|
||||||
def test_en_redirect(self):
|
def test_en_redirect(self):
|
||||||
response = self.client.get('/account/register', HTTP_ACCEPT_LANGUAGE='en')
|
response = self.client.get('/account/register-without-slash', HTTP_ACCEPT_LANGUAGE='en')
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertRedirects(response, '/en/account/register-without-slash', 302)
|
||||||
self.assertEqual(response['location'], 'http://testserver/en/account/register')
|
|
||||||
|
|
||||||
response = self.client.get(response['location'])
|
response = self.client.get(response['location'])
|
||||||
self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
|
||||||
class URLResponseTests(URLTestCaseBase):
|
class URLResponseTests(URLTestCaseBase):
|
||||||
|
|
|
@ -7,4 +7,5 @@ view = TemplateView.as_view(template_name='dummy.html')
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(_(r'^register/$'), view, name='register'),
|
url(_(r'^register/$'), view, name='register'),
|
||||||
|
url(_(r'^register-without-slash$'), view, name='register-without-slash'),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue