-
-
Save Sepero/2204099 to your computer and use it in GitHub Desktop.
""" Author contact: sepero 111 @ gmail . com """ | |
from django import http | |
from django.utils.http import urlquote | |
from django.core import urlresolvers | |
class RemoveSlashMiddleware(object): | |
""" | |
This middleware works like django's built in APPEND_SLASH, but in reverse. Eg | |
It removes all ending slashes from a URL, and if that doesn't resolve, it will add one slash and try again. | |
Set APPEND_SLASH to False when using this middleware. | |
Forked from the original code at http://gregbrown.co.nz/code/append-or-remove-slash/ | |
""" | |
def process_request(self, request): | |
# check if the url is valid | |
path = new_path = request.path_info | |
# Remove all trailing slashes from new_path. | |
while new_path.endswith('/'): | |
new_path = new_path[:-1] | |
urlconf = getattr(request, 'urlconf', None) | |
if not _is_valid_path(new_path, urlconf): | |
# If removing slashes made new_path invalid, add one slash and try again. | |
new_path = new_path + '/' | |
if path != new_path and _is_valid_path(new_path, urlconf): | |
return self.adjust_path(request, new_path) | |
elif path != new_path: | |
# If new_path is valid and not eq to path, send a permanent redirect. | |
return self.adjust_path(request, new_path) | |
def adjust_path(self, request, new_path): | |
""" | |
Redirect the clients browser to new_path, and tell it that all future requests to the desired URL should be sent to new_path. (This method looks like it may be able to be made more efficient, but I'm not familiar enough with request.path_info and other django variables to know how.) | |
""" | |
if request.get_host(): | |
new_url = "%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), urlquote(new_path)) | |
else: | |
new_url = urlquote(new_path) | |
if request.GET: | |
new_url += '?' + request.META['QUERY_STRING'] | |
return http.HttpResponseRedirect(new_url) | |
def _is_valid_path(path, urlconf=None): | |
""" | |
Returns True if the given path resolves against the default URL resolver, | |
False otherwise. | |
""" | |
try: | |
urlresolvers.resolve(path, urlconf) | |
return True | |
except urlresolvers.Resolver404: | |
return False | |
""" Author contact: sepero 111 @ gmail . com """ |
Writing this for those that are building an API with Django and ended up here because of trailing slash issues.
When you write an API, the APPEND_SLASH
behavior is problematic because you don't always control what the client will request, and you don't want redirects (where you loose POST body) nor do you want 404s.
The solution is pretty simple in this case actually. You don't care about your URL being unique, you don't care about SEO: you're building an API. So you're fine with your URLs being accessible with both a slash and without it. Which would be an SEO issue (duplicate content) and the reason why APPEND_SLASH
was introduced. But you don't care, so no need to use the gist here and try to do the opposite (redirect slash urls to urls without a slash) either.
All you need to do is:
- have
APPEND_SLASH
set to False - Define a middleware that will add a slash internally to any request that don't end with one
- Define all your URLs with a trailing slash
def add_slash(get_response):
def middleware(request):
if not request.path.endswith('/'):
request.path_info = request.path = f"{request.path}/"
return get_response(request)
return middleware
MIDDLEWARE = [
# ...
'yourapp.middleware.add_slash',
# ...
]
It's easier to:
In nginx.