URL-адреса django без косой черты не перенаправляют

89

У меня есть два приложения, расположенные на двух разных компьютерах. На компьютере А в urls.pyфайле есть такая строка:

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

И этот URL-адрес будет работать как для, так mySite.com/cast/и для mySite.com/cast. Но на компьютере BI есть похожий URL-адрес, записанный как:

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

По какой-то причине на компьютере B url mySite.com/login/ будет работать, но mySite.com/loginбудет зависать и не будет возвращаться, mySite.com/login/как на компьютере A. Что-то я пропустил? Оба url.pyфайла мне кажутся идентичными.

что-что
источник

Ответы:

103

проверьте свои APPEND_SLASHнастройки в файле settings.py

больше информации в django docs

Jiaaro
источник
4
"Если задано значение True, если URL-адрес запроса не соответствует ни одному из шаблонов в URLconf и не заканчивается косой чертой, выполняется перенаправление HTTP на тот же URL-адрес с добавленной косой чертой. Обратите внимание, что перенаправление может вызвать любые данные, отправленные в запросе POST, будут потеряны. ". «Настройка APPEND_SLASH используется, только если установлено CommonMiddleware ...». Я предпочитаю ответ Майкла Гендина за более чистое решение.
Wtower
3
Это не сработает, если вы используете дополнительный URL "перехвата всех" в последней записи ваших шаблонов URL. Ответ @ speedplane будет работать даже в этих ситуациях. Но, конечно, это проще, и его следует использовать, если нет записей urlpattern "поймать все".
np8
195

Или вы можете написать свои URL-адреса следующим образом:

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

Знак вопроса после косой черты делает его необязательным в регулярном выражении. Используйте его, если по каким-то причинам вы не хотите использовать настройку APPEND_SLASH.

Майкл Гендин
источник
12
Назовите меня наивным, но почему этот ответ не получил миллиона голосов и не получил записи в часто задаваемых вопросах по django?
Фергал Моран,
42
Совершенно уверен, что вы не хотите этого делать по причинам SEO - лучше перенаправить на канонический URL, чем иметь два действительных URL.
Брайан Франц,
47
Если вы создаете RESTful API с помощью Django, это может быть хорошим решением, когда разработчики отправляют данные POST непосредственно на URL-адрес конечной точки. При использовании APPEND_SLASH, если они случайно отправили его без завершающей косой черты, а ваш urlconf имеет завершающую косую черту, они получат исключение о потере данных при перенаправлении запросов POST.
OrPo
5
Проблема с этим решением заключается в том, что вы обслуживаете одну и ту же страницу под двумя URL-адресами (с конечными и без них /) - небрежно, плохо для поисковых роботов, сложнее в обслуживании, сложнее перейти на новую систему (так как это так легко не заметить)
Jiaaro
Хороший ответ. Я бы предпочел запретить косую черту (поскольку она означала начало чего-то нового, а не конец чего-то (например, / etc), но это позволяет использовать стандартный (/ view) и нестандартный (/ view /).
Дэвид Бец
19

Это улучшает ответ @Michael Gendin. Его ответ обслуживает идентичную страницу с двумя отдельными URL-адресами. Было бы лучше иметь loginавтоматическое перенаправление login/, а затем использовать последнюю в качестве главной страницы:

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"),
)
скоростной самолет
источник
Очень полезно, если в конце есть универсальный URL.
thclark
Как это могло работать с регулярными выражениями? Если исходный URL-адрес соответствует регулярному выражению с именем клиента, например
Николо Гаспарини
@ NicolòGasparini - более новые версии Django имеют pattern_nameаргумент, который используется redirectвместе со всеми сопоставленными аргументами URL.
Тим Тисдалл,
2

У меня тоже была такая же проблема. Мое решение было помещено (| /) перед конечной строкой моего регулярного выражения.

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

Атауальпа Сильва Фалькон
источник
1

Добавьте косую черту без перенаправления , используйте ее вместо CommonMiddleware в настройках Django 2.1:

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

Добавьте в свой основной каталог приложения 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

У меня была такая же проблема. В моем случае это был устаревший остаток какой-то старой версии в urls.py, до создания статических файлов:

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

MEDIA_URL пуст, поэтому этот шаблон соответствует всему.

janek37
источник