Django-URLs ohne abschließenden Schrägstrich werden nicht umgeleitet

86

Ich habe zwei Anwendungen auf zwei separaten Computern. Auf Computer A habe urls.pyich in der Datei eine Zeile wie die folgende:

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

Und diese URL funktioniert sowohl für mySite.com/cast/als auch mySite.com/cast. Aber auf Computer BI haben Sie eine ähnliche URL geschrieben wie:

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

Aus irgendeinem Grund url mySite.com/loginfunktioniert das / auf Computer B , mySite.com/loginhängt aber und wird nicht zurückgeleitet, mySite.com/login/wie es auf Computer A der Fall ist. Gibt es etwas, das ich verpasst habe? Beide url.pyDateien sehen für mich identisch aus.

was was
quelle

Antworten:

101

Überprüfen Sie Ihre APPEND_SLASHEinstellung in der Datei settings.py

Weitere Infos in den Django-Dokumenten

Jiaaro
quelle
4
"Wenn die Anforderungs-URL auf True gesetzt ist und mit keinem der Muster in der URLconf übereinstimmt und nicht mit einem Schrägstrich endet, wird eine HTTP-Umleitung an dieselbe URL mit einem angehängten Schrägstrich ausgegeben. Beachten Sie, dass die Umleitung dazu führen kann Alle in einer POST-Anfrage übermittelten Daten gehen verloren. " "Die Einstellung APPEND_SLASH wird nur verwendet, wenn CommonMiddleware installiert ist ...". Ich bevorzuge Michael Gendins Antwort für eine sauberere Lösung.
Wtower
2
Dies funktioniert nicht, wenn Sie beim letzten Eintrag Ihrer URL-Muster eine zusätzliche URL "Alle abfangen" verwenden. Die Antwort von @ speedplane funktioniert auch in diesen Situationen. Dies ist natürlich einfacher und sollte verwendet werden, wenn keine URL-Mustereinträge "Alle abfangen" vorhanden sind.
np8
192

Oder Sie können Ihre URLs folgendermaßen schreiben:

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

Das Fragezeichen nach dem abschließenden Schrägstrich macht es in regulärem Ausdruck optional. Verwenden Sie diese Option, wenn Sie aus bestimmten Gründen die Einstellung APPEND_SLASH nicht verwenden möchten.

Michael Gendin
quelle
11
Nennen Sie mich naiv - aber warum hat diese Antwort nicht eine Million positive Stimmen und einen Eintrag in der Django-FAQ?
Fergal Moran
41
Ich bin mir ziemlich sicher, dass Sie dies aus SEO-Gründen nicht tun möchten - es ist besser, zu einer kanonischen URL umzuleiten, als zwei gültige URLs zu haben.
Brian Frantz
46
Wenn Sie eine RESTful-API mit Django erstellen, kann dies eine gute Lösung sein, wenn Entwickler Daten direkt an die Endpunkt-URL senden. Wenn sie es verwenden APPEND_SLASH, wenn sie es versehentlich ohne abschließenden Schrägstrich gesendet haben und Ihre urlconf MIT einem abschließenden Schrägstrich ist, erhalten sie eine Ausnahme über Datenverluste, wenn sie POST-Anforderungen umleiten.
OrPo
5
Das Problem bei dieser Lösung ist, dass Sie dieselbe Seite unter 2 URLs (mit und ohne Trailing /) bereitstellen - schlampig, schlecht für Crawler, schwieriger zu warten, schwieriger auf ein neues System zu migrieren (da es so leicht zu übersehen ist)
Jiaaro
Gute Antwort. Ich würde es vorziehen, den Schrägstrich nicht zuzulassen (da dies den Beginn von etwas Neuem bedeutet, nicht das Ende von etwas (z. B. / etc), aber dies ermöglicht den Standard (/ view) und den Nicht-Standard (/ view /).
David Betz
18

Dies verbessert die Antwort von @Michael Gendin. Seine Antwort dient der identischen Seite mit zwei separaten URLs. Es wäre besser, loginautomatisch umzuleiten login/und diese dann als Hauptseite zu verwenden:

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"),
)
Geschwindigkeitsflugzeug
quelle
Sehr nützlich, wenn Sie am Ende eine Sammel-URL haben.
Park
Wie könnte das mit Regexs funktionieren? Wenn die ursprüngliche URL beispielsweise mit einem regulären Ausdruck mit einem Kundennamen übereinstimmt
Nicolò Gasparini
2

Ich hatte auch das gleiche Problem. Meine Lösung wurde mit einem (| /) vor die Endzeile meines regulären Ausdrucks gesetzt.

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

Atahualpa Silva Falcón
quelle
1

Fügen Sie einen Schrägstrich ohne Weiterleitung hinzu und verwenden Sie ihn anstelle von CommonMiddleware in den Einstellungen, Django 2.1:

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

Fügen Sie Ihrem Haupt-App-Verzeichnis middleware.py hinzu :

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
Max Tkachenko
quelle
0

Ich hatte das gleiche Problem. In meinem Fall war es ein veralteter Rest einer alten Version in urls.py vor statischen Dateien:

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

MEDIA_URL war leer, daher stimmte dieses Muster mit allem überein.

janek37
quelle