At Now when url is opened (without a slash - example.com/blog
), a slash is automatically added at the end (there are 301
redirects). The question is, can I somehow do it so that the check first goes to see if the page exists (without a slash - example.com/blog
). If so, open it. If not, then check whether the page exists with a slash (only without 301
- example.com/blog/
). If so, then redirect 301
, and if not, then throw 404
.
Now just if there is no page (example.com/blog
), then a slash is added to the end first (example.com/blog/
), 301
redirects go and only then a 404
error is thrown. In this case, the 404
error must be thrown immediately, without a 301
redirect.
The dispatch
was rewritten as follows.
def is_normal_slash_count(url):
temp_url = url
slash_count = 0
while temp_url.endswith('/'):
slash_count += 1
temp_url = temp_url[:-1]
return (slash_count == 1, slash_count)
def replace_bad_slash(url, slash_count):
if slash_count == 2:
return url.replace('//', '/')
return url.replace('/'*(slash_count-1), '')
def normalize_url(url):
if len(url) > 1:
if not url.endswith('/'):
return url + '/'
# replace the url like /contacts//// to /contacts/
good_slash, slash_count = is_normal_slash_count(url)
if not good_slash:
url = replace_bad_slash(url, slash_count)
return url
def is_bad_url(url):
if len(url) > 1:
good_slash, slash_count = is_normal_slash_count(url)
if not good_slash:
return True
return False
class RedirectMixinView:
def dispatch(self, *args, **kwargs):
url = self.request.path
redirect_setting = RedirectSettings.objects.filter(url_from=url).first()
if redirect_setting:
return redirect(redirect_setting.url_to, permanent=True)
if is_bad_url(url):
return redirect(normalize_url(url), permanent=True)
return super(RedirectMixinView, self).dispatch(*args, **kwargs)
Is this realistic?
I think in the direction of writing middleware
.
Updated
projects.urls
url(r'^page/', include('pages.urls')),
pages.urls
url(r'^$', PageView.as_view(), name='page'),
test
try:
resolve('/page/')
except:
raise Http404
return redirect('/page/')
I'm tried /page/, /page, page/, page, http://127.0.0.1:8000/page/, http://127.0.0.1:8000/page
RedirectMixinView
from LandingView
.CommonMiddleware
.RedirectMiddleware
to the list of middleware (preferably in the top).RedirectMiddleware
The code is written jointly with @dirkgroten (most of his contribution).
import re
from django.http import HttpResponsePermanentRedirect
class RedirectMiddleware(object):
response_redirect_class = HttpResponsePermanentRedirect
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
path = re.sub("/+", "/", request.path)
if response.status_code == 404:
if not path.endswith('/'):
request.path = path # to force using the cleaned path
else:
request.path = path[:-1] # to force using the cleaned path
try:
full_path = request.get_full_path(force_append_slash=True) # add the slash, keeping query parameters
r = resolve(full_path)
new_response = r.func(request, args=r.args, kwargs=r.kwargs)
if new_response.status_code == 200:
return redirect(full_path)
except Resolver404:
pass # this will fall through to `return response`
# Add the Content-Length header to non-streaming responses if not
# already set.
if not response.streaming and not response.has_header('Content-Length'):
response['Content-Length'] = str(len(response.content))
return response
if ($request_uri ~* "\/\/") {
rewrite ^/(.*) $scheme://$host/$1 permanent;
}
# merge_slashes off;
It does what you need, and also removes duplicate slashes if this page exists.