The Python Oracle

Django routing with VueJS single page has unexpected behaviour when there is no trailing slash in the URL

This video explains
Django routing with VueJS single page has unexpected behaviour when there is no trailing slash in the URL

--

Become part of the top 3% of the developers by applying to Toptal
https://topt.al/25cXVn

Music by Eric Matyas
https://www.soundimage.org

Track title: Secret Catacombs

Full question
https://stackoverflow.com/questions/5897...

Question links:
[this]: https://stackoverflow.com/questions/4286...

Accepted answer links:
[Temporary Redirect 307]: https://developer.mozilla.org/en-US/docs...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Chapters
00:00 Question
02:42 Accepted answer
05:13 Thank you

--

Tags
#python #django #vue.js #routing #trailingslash



ACCEPTED ANSWER

Score 0


The problem is that you have a catch-all in re_path(r'^.*$', views.vue), so if any URL is not matched exactly on the earlier paths, this will be triggered.

Django's CommonMiddleware actually appends a trailing slash and redirect, when it finds a 404 and the URL path does not end in / (depending on the APPEND_SLASH setting), but that's on response.

In you case, you can have a tiny request middleware that appends trailing slash if the request path not end in / e.g.:

from django.shortcuts import redirect

class AppendTrailingSlashOnRequestMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        if not request.path.endswith('/'):
            query_string = request.META['QUERY_STRING']
            query_string = f'?{query_string}' if query_string else ''
            to_url = f'{request.path}/{query_string}'
            return redirect(to_url, permanent=True)

        response = self.get_response(request)

        return response

Add the middleware to settings.MIDDLEWARE obviously, preferably put it at the top to prevent unnecessarily processing from other middlewares as we'll be redirecting anyways and processing would be required then as well.


But this has an issue; the data from POST/PUT/PATCH will be lost when doing redirection (here we're doing 301 but similarly applicable for 302. There's Temporary Redirect 307 that can help us in this regard and the good thing is all the regular browsers including IE support this. But Django does not have this out of the box; so we need to implement this ourselves:

from django.http.response import HttpResponseRedirectBase

class HttpTemporaryResponseRedirect(HttpResponseRedirectBase):
    status_code = 307

Now, import that in the middleware, and use it instead of redirect:

class AppendTrailingSlashOnRequestMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        if not request.path.endswith('/'):
            query_string = request.META['QUERY_STRING']
            query_string = f'?{query_string}' if query_string else ''
            to_url = f'{request.path}/{query_string}'
            return HttpTemporaryResponseRedirect(to_url)  # here

        response = self.get_response(request)

        return response

N.B: If you want to keep the browser caching facilities for GET, you can redirect to 301/307 based on request.method.