Adresy URL django bez końcowego ukośnika nie powodują przekierowań


89

Mam dwie aplikacje umieszczone na dwóch oddzielnych komputerach. Na komputerze A w urls.pypliku mam wiersz podobny do następującego:

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

Ten adres URL będzie działać zarówno w przypadku, jak mySite.com/cast/i mySite.com/cast. Ale na komputerze BI mają podobny adres URL zapisany na przykład:

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

Z jakiegoś powodu na komputerze B url mySite.com/login/ będzie działał, ale mySite.com/loginbędzie się zawieszał i nie przekieruje z powrotem do mySite.com/login/takiego, jak na komputerze A. Czy jest coś, co przegapiłem? Oba url.pypliki wyglądają identycznie jak ja.

Odpowiedzi:


103

sprawdź swoje APPEND_SLASHustawienia w pliku settings.py

więcej informacji w dokumentacji django


4
„Po ustawieniu na True, jeśli adres URL żądania nie pasuje do żadnego ze wzorców w URLconf i nie kończy się ukośnikiem, na ten sam adres URL jest wysyłane przekierowanie HTTP z dodanym ukośnikiem. Pamiętaj, że przekierowanie może powodować wszelkie dane przesłane w żądaniu POST, które mają zostać utracone. ”. „Ustawienie APPEND_SLASH jest używane tylko wtedy, gdy zainstalowano CommonMiddleware ...”. Wolę odpowiedź Michaela Gendina za czystsze rozwiązanie.
— Wtower

3
To nie zadziała, jeśli używasz dodatkowego adresu URL typu „catch all” w ostatnim wpisie swoich wzorców url. Odpowiedź @ speedplane zadziała nawet w takich sytuacjach. Ale oczywiście jest to prostsze i powinno być używane, jeśli nie ma wpisów typu „catch all” ze wzorami url.
— np8

195

Lub możesz napisać swoje adresy URL w ten sposób:

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

Znak zapytania po końcowym ukośniku sprawia, że ​​jest on opcjonalny w wyrażeniu regularnym. Użyj go, jeśli z jakichś powodów nie chcesz używać ustawienia APPEND_SLASH.


12
Nazwijcie mnie naiwnym - ale dlaczego ta odpowiedź nie ma miliona głosów za i wpisu w FAQ django?
— Fergal Moran

42
Na pewno nie chcesz tego robić ze względów SEO - lepiej przekierować do kanonicznego adresu URL niż mieć dwa prawidłowe adresy URL.
— Brian Frantz

47
Jeśli tworzysz RESTful API przy użyciu Django, może to być dobre rozwiązanie, gdy programiści przesyłają dane bezpośrednio do adresu URL punktu końcowego. Podczas używania APPEND_SLASH, jeśli przypadkowo wysłali go bez końcowego ukośnika, a twój urlconf jest Z końcowym ukośnikiem, otrzymaliby wyjątek dotyczący utraty danych podczas przekierowywania żądań POST.
— OrPo

5
Problem z tym rozwiązaniem polega na tym, że udostępniasz tę samą stronę pod 2 adresami URL (z końcowymi adresami URL i bez nich /) - niechlujnie, źle dla robotów, trudniejsza w utrzymaniu, trudniejsza do migracji do nowego systemu (ponieważ tak łatwo ją przeoczyć)
— Jiaaro

Dobra odpowiedź. Wolałbym nie zezwalać na ukośnik (ponieważ oznaczał początek czegoś nowego, a nie koniec czegoś (np. / Etc), ale pozwala to na standardowe (/ view) i niestandardowe (/ view /).
— David Betz

19

To poprawia odpowiedź @Michaela Gendina. Jego odpowiedź zawiera identyczną stronę z dwoma oddzielnymi adresami URL. Lepiej byłoby mieć loginautomatyczne przekierowanie do login/, a następnie wyświetlać tę ostatnią jako stronę główną:

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"),
)

Bardzo przydatne, gdy na końcu masz adres URL typu catch-all.
— thclark

Jak to działa z wyrażeniami regularnymi? Jeśli oryginalny adres URL pasuje do wyrażenia regularnego z nazwą klienta, na przykład
— Nicolò Gasparini

@ NicolòGasparini - nowsze wersje Django mają pattern_nameargument, który jest używany redirectwraz ze wszystkimi dopasowanymi argumentami adresu URL.
— Tim Tisdall

2

Ja też miałem ten sam problem. Moje rozwiązanie zostało wstawione (| /) przed końcową linią wyrażenia regularnego.

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


1

Dołącz ukośnik bez przekierowania , użyj go zamiast CommonMiddleware w ustawieniach, Django 2.1:

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

Dodaj do głównego katalogu aplikacji 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

Miałem ten sam problem. W moim przypadku była to nieaktualna pozostałość po starej wersji w urls.py, sprzed staticfiles:

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

MEDIA_URL był pusty, więc ten wzorzec pasuje do wszystkiego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.