Gli URL Django senza una barra finale non vengono reindirizzati


89

Ho due applicazioni posizionate su due computer separati. Sul computer A, nel urls.pyfile ho una riga come la seguente:

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

E quell'URL funzionerà per entrambi mySite.com/cast/e mySite.com/cast. Ma sul computer BI hanno un URL simile scritto come:

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

Per qualche motivo sul computer B url mySite.com/login/ funzionerà ma si mySite.com/loginbloccherà e non tornerà indietro mySite.com/login/come sul computer A. C'è qualcosa che mi sono perso? Entrambi i url.pyfile mi sembrano identici.

Risposte:


103

controlla le tue APPEND_SLASHimpostazioni nel file settings.py

maggiori informazioni nella documentazione di django


4
"Quando è impostato su True, se l'URL della richiesta non corrisponde a nessuno dei pattern in URLconf e non termina con una barra, viene emesso un reindirizzamento HTTP allo stesso URL con una barra aggiunta. Tieni presente che il reindirizzamento potrebbe causare tutti i dati inviati in una richiesta POST andranno persi. ". "L'impostazione APPEND_SLASH viene utilizzata solo se CommonMiddleware è installato ...". Preferisco la risposta di Michael Gendin per una soluzione più pulita.
Wtower

3
Questo non funziona se stai usando un URL aggiuntivo "catch all" nell'ultima voce dei tuoi urlpatterns. La risposta di @ speedplane funzionerà anche in quelle situazioni. Ma, ovviamente, questo è più semplice e dovrebbe essere usato se non ci sono voci "catch all" urlpattern.
np8

195

Oppure puoi scrivere i tuoi URL in questo modo:

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

Il segno della domanda dopo la barra finale lo rende facoltativo in regexp. Usalo se per qualche motivo non vuoi usare l'impostazione APPEND_SLASH.


12
Chiamami ingenuo, ma perché questa risposta non ha ottenuto un milione di voti positivi e una voce nelle faq di django?
Fergal Moran

42
Abbastanza sicuro che non vuoi farlo per motivi SEO: meglio reindirizzare a un URL canonico piuttosto che avere due URL validi.
Brian Frantz

47
Se stai creando un'API RESTful utilizzando Django, questa può essere una buona soluzione quando gli sviluppatori POST i dati direttamente all'URL dell'endpoint. Durante l'utilizzo APPEND_SLASH, se lo inviano accidentalmente senza la barra finale e il tuo urlconf è CON una barra finale, otterrebbero un'eccezione sulla perdita di dati durante il reindirizzamento delle richieste POST.
OrPo

5
Il problema con questa soluzione è che stai servendo la stessa pagina sotto 2 URL (con e senza il finale /) - sciatto, dannoso per i crawler, più difficile da mantenere, più difficile da migrare a un nuovo sistema (poiché è così facile trascurare)
Jiaaro

Buona risposta. Preferirei disabilitare la barra (poiché indicava l'inizio di qualcosa di nuovo, non la fine di qualcosa (ad esempio / etc), ma questo consente lo standard (/ view) e il non standard (/ view /).
David Betz

19

Questo migliora la risposta di @Michael Gendin. La sua risposta serve la stessa pagina con due URL separati. Sarebbe meglio avere loginil reindirizzamento automatico a login/e quindi servire quest'ultimo come pagina principale:

from django.conf.urls import patterns
from django.views.generic import RedirectView

urlpatterns = patterns('',
    # Redirect login to login/
    (r'^login$', RedirectView.as_view(url = '/login/')),
    # Handle the page with the slash.
    (r'^login/', "views.my_handler"),
)

Molto utile quando hai un URL catch-all alla fine.
thclark

Come potrebbe funzionare con le regex? Se l'URL originale corrisponde a un'espressione regolare con il nome di un cliente, ad esempio
Nicolò Gasparini

@ NicolòGasparini - le versioni più recenti di Django hanno un pattern_namearg che viene utilizzato redirectinsieme a tutti gli argomenti URL che corrispondono.
Tim Tisdall,

2

Anch'io ho avuto lo stesso problema. La mia soluzione è stata messa un (| /) prima della riga finale della mia espressione regolare.

url(r'^artists/(?P[\d]+)(|/)$', ArtistDetailView.as_view()),


1

Aggiungi barra senza reindirizzamento , usalo al posto di CommonMiddleware nelle impostazioni, Django 2.1:

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

Aggiungi alla directory principale dell'app middleware.py :

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


class HttpSmartRedirectResponse(HttpResponsePermanentRedirect):
    pass


class CommonMiddlewareAppendSlashWithoutRedirect(CommonMiddleware):
    """ This class converts HttpSmartRedirectResponse to the common response
        of Django view, without redirect.
    """
    response_redirect_class = HttpSmartRedirectResponse

    def __init__(self, *args, **kwargs):
        # create django request resolver
        self.handler = BaseHandler()

        # prevent recursive includes
        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 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 + '/'
            # we don't need query string in path_info because it's in request.GET already
            request.path_info = request.path
            response = self.handler.get_response(request)

        return response

0

Ho avuto lo stesso problema. Nel mio caso si trattava di un residuo vecchio di una vecchia versione in urls.py, prima di staticfiles:

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

MEDIA_URL era vuoto, quindi questo pattern corrispondeva a tutto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.