107 votes

Les URL Django sans barre oblique finale ne redirigent pas

J'ai deux applications situées sur deux ordinateurs distincts. Sur l'ordinateur A, dans le fichier urls.py , j'ai une ligne comme celle-ci:

(r'^cast/$', 'mySite.simulate.views.cast')

Et cette URL fonctionnera à la fois pour mySite.com/cast/ et mySite.com/cast . Mais sur l'ordinateur B, j'ai une URL similaire écrite comme suit:

(r'^login/$', 'mySite.myUser.views.login')

Pour une raison quelconque, sur l'ordinateur B, l'URL mySite.com/login fonctionnera mais mySite.com/login va bloquer et ne redirigera pas vers mySite.com/login/ comme sur l'ordinateur A. Est-ce que j'ai manqué quelque chose? Les fichiers urls.py me semblent identiques.

1voto

Tehdrew Points 426

Dans certains cas, nous avons des problèmes lorsque certains de nos utilisateurs appellent l'API avec des terminaisons différentes. Habituellement, nos utilisateurs utilisent Postman pour cela et ne se préoccupent pas de la barre oblique à la fin. En conséquence, nous recevons des demandes de problème de support, car les utilisateurs ont oublié d'ajouter une barre oblique / à la fin des requêtes POST.

Nous l'avons résolu en utilisant un middleware personnalisé qui fonctionne pour nous dans Django 3.2+ et Django 4.0+. Après cela, Django peut gérer toutes les requêtes POST/PUT/DELETE vers votre API avec ou sans barre oblique. Avec ce middleware, il n'est pas nécessaire de modifier la propriété APPEND_SLASH dans settings.py

Donc, dans le settings.py, il faut supprimer votre 'django.middleware.common.CommonMiddleware' actuel et insérer un nouveau middleware. Assurez-vous de changer your_project_name dans mon exemple ci-dessous par le nom réel de votre projet.

MIDDLEWARE = [
...
  # 'django.middleware.common.CommonMiddleware',
    'your_project_name.middleware.CommonMiddlewareAppendSlashWithoutRedirect',
...
]

Ajoutez au répertoire de votre application principale middleware.py:

from django.http import HttpResponsePermanentRedirect, HttpRequest
from django.core.handlers.base import BaseHandler
from django.middleware.common import CommonMiddleware
from django.utils.http import escape_leading_slashes
from django.conf import settings

class HttpSmartRedirectResponse(HttpResponsePermanentRedirect):
    pass

class CommonMiddlewareAppendSlashWithoutRedirect(CommonMiddleware):
    """ Cette classe convertit HttpSmartRedirectResponse en la réponse commune
             de la vue Django, sans redirection. C'est nécessaire pour faire correspondre les codes d'état
             pour les urls comme /url?q=1 et /url/?q=1. Si vous ne l'utilisez pas, vous aurez toujours le code 302
             sur les pages sans barre oblique.
     """
    response_redirect_class = HttpSmartRedirectResponse

def __init__(self, *args, **kwargs):
    # créer un résolveur de requête django
    self.handler = BaseHandler()

    # empêcher les inclusions récursives
    old = settings.MIDDLEWARE
    name = self.__module__ + '.' + self.__class__.__name__
    settings.MIDDLEWARE = [i for i in settings.MIDDLEWARE if i != name]

    self.handler.load_middleware()

    settings.MIDDLEWARE = old
    super(CommonMiddlewareAppendSlashWithoutRedirect, self).__init__(*args, **kwargs)

def get_full_path_with_slash(self, request):
    """ Renvoie le chemin complet de la requête avec une barre oblique ajoutée à la fin
         sans Exception en mode Debug
    """
    new_path = request.get_full_path(force_append_slash=True)
    # Empêcher la construction d'urls relatives au schéma.
    new_path = escape_leading_slashes(new_path)
    return new_path

def process_response(self, request, response):
    response = super(CommonMiddlewareAppendSlashWithoutRedirect, self).process_response(request, response)

    if isinstance(response, HttpSmartRedirectResponse):
        if not request.path.endswith('/'):
            request.path = request.path + '/'
        # nous n'avons pas besoin de chaîne de requête dans path_info car elle est déjà dans request.GET
        request.path_info = request.path
        response = self.handler.get_response(request)

    return response

Cette réponse peut ressembler à la réponse de Max Tkachenko. Mais son code n'a pas fonctionné pour moi dans les dernières versions de Django.

0voto

janek37 Points 58

J'ai eu le même problème. Dans mon cas, c'était un reste obsolète d'une ancienne version dans urls.py, avant staticfiles :

url(r'^%s(?P.*)$' % settings.MEDIA_URL.lstrip('/'),
    'django.views.static.serve',
    kwargs={'document_root': settings.MEDIA_ROOT}),

MEDIA_URL était vide, donc ce motif correspondait à tout.

0voto

Mahendra Points 41

Dans le fichier de configuration de votre projet Django (settings.py), assurez-vous que le paramètre APPEND_SLASH est défini sur True :

APPEND_SLASH = True

Ensuite, ajoutez CommonMiddleware au paramètre MIDDLEWARE :

MIDDLEWARE = [
    # autres classes middleware...
    'django.middleware.common.CommonMiddleware',
    # autres classes middleware...
]

Avec cette configuration, CommonMiddleware de Django gérera automatiquement la redirection d'URL et ajoutera ou supprimera le slash terminal selon les besoins. Cela signifie que les requêtes vers 'xyz/' et 'xyz' seront correctement traitées par Django REST Framework.

Remarque - Lors de l'envoi de requêtes, il est généralement recommandé d'inclure le slash terminal pour respecter les conventions RESTful. Cependant, avec la configuration ci-dessus, les requêtes sans le slash terminal fonctionneront également comme prévu.

0voto

forward Points 36

Vous pouvez utiliser ce fragment de code

 from django.urls import re_path, path
 from django.urls.resolvers import _route_to_regex
 def tr_path(route, view, *args, **kwargs):
    if callable(view):
       r = _route_to_regex(route)
       if r[0][-1] != '/':
          new_route = r[0] + '/?$'  # ajoute une barre oblique
          return re_path(new_route, view, *args, **kwargs)
    return path(route, view, *args, **kwargs)

puis remplacez votre path par tr_path

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X