Skip to content

Instantly share code, notes, and snippets.

@Sepero
Forked from gregplaysguitar/slash_middleware.py
Created March 26, 2012 09:17
Show Gist options
  • Save Sepero/2204099 to your computer and use it in GitHub Desktop.
Save Sepero/2204099 to your computer and use it in GitHub Desktop.
Remove slashs in django - like APPEND_SLASH but opposite.
""" 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 """
@davidrenne
Copy link

It's easier to:

rewrite ^(.+)/+$ $1 permanent;

In nginx.

@lapinvert
Copy link

lapinvert commented Aug 17, 2024

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:

  1. have APPEND_SLASH set to False
  2. Define a middleware that will add a slash internally to any request that don't end with one
  3. 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',
    # ...
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment