在不打开的情况下检查 url 状态

Check url status without opening it

现在当 url 打开时(没有斜线 - example.com/blog),斜线会自动添加到末尾(有 301 重定向)。问题是,我能否以某种方式执行此操作,以便首先检查该页面是否存在(没有斜杠 - example.com/blog)。如果是这样,请打开它。如果没有,则检查页面是否存在带斜杠的(只有不带301 - example.com/blog/)。如果是,则重定向 301,如果不是,则抛出 404.

现在如果没有页面(example.com/blog),那么先在末尾添加一个斜线(example.com/blog/),301 重定向去然后才一个404 抛出错误。在这种情况下,必须立即抛出 404 错误,而无需 301 重定向。

dispatch改写如下。

def is_normal_slash_count(url):
    temp_url = url
    slash_count = 0
    while temp_url.endswith('/'):
        slash_count += 1
        temp_url = temp_url[:-1]
    return (slash_count == 1, slash_count)


def replace_bad_slash(url, slash_count):
    if slash_count == 2:
        return url.replace('//', '/')
    return url.replace('/'*(slash_count-1), '')


def normalize_url(url):
    if len(url) > 1:
        if not url.endswith('/'):
            return url + '/'
        # replace the url like /contacts//// to /contacts/
        good_slash, slash_count = is_normal_slash_count(url)
        if not good_slash:
            url = replace_bad_slash(url, slash_count)
    return url

def is_bad_url(url):
    if len(url) > 1:
        good_slash, slash_count = is_normal_slash_count(url)
        if not good_slash:
            return True
    return False

class RedirectMixinView:

    def dispatch(self, *args, **kwargs):
        url = self.request.path

        redirect_setting = RedirectSettings.objects.filter(url_from=url).first()
        if redirect_setting:
            return redirect(redirect_setting.url_to, permanent=True)

        if is_bad_url(url):
            return redirect(normalize_url(url), permanent=True)
        return super(RedirectMixinView, self).dispatch(*args, **kwargs)

这现实吗?

我想写middleware的方向。

已更新

projects.urls

url(r'^page/', include('pages.urls')),

pages.urls

url(r'^$', PageView.as_view(), name='page'),

测试

try:
    resolve('/page/')
except:
    raise Http404
return redirect('/page/')

我试过了/page/, /page, page/, page, http://127.0.0.1:8000/page/, http://127.0.0.1:8000/page

首先确保在 settings.py 中将 APPEND_SLASH 设置为 False。这将禁用自动 301 重定向到带有斜杠的 URLs。

然后在重定向前用resolve()检查带斜线的URL是否存在。在处理 response 状态码为 404 的情况的中间件 class 中执行此操作。

from django.urls import resolve

try:
    resolve(url_with_slash)
except Resolver404:
    raise Http404
return redirect(url_with_slash)

请注意,当存在与 url 匹配的路径时,resolve(url) 不会引发异常,即使该视图之后可能仍会引发 404。例如,如果您有a DetailView 表示对象的 pk 在 URL 中。假设您将 /objects/<pk>/ 作为显示对象的路径,那么 url /objects/4/ 将始终匹配,即使 pk=4 的对象不存在。重定向后视图仍会引发 404。

所以如果你真的想同时捕获那些 404,你实际上可以自己调用视图函数来检查响应:

try:
    r = resolve(url_with_slash)
    response = r.func(request, args=r.args, kwargs=r.kwargs)
    if response.status_code == 200:
         return redirect(url_with_slash)
except Resolver404:
    pass
  1. 您需要从 LandingView 中删除 RedirectMixinView
  2. 注释掉中间件 CommonMiddleware.
  3. RedirectMiddleware 添加到 中间件列表 (最好在顶部)。
  4. 创建RedirectMiddleware

代码是与@dirkgroten共同编写的(他的大部分贡献)。

import re
from django.http import HttpResponsePermanentRedirect

class RedirectMiddleware(object):
    response_redirect_class = HttpResponsePermanentRedirect

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):

        response = self.get_response(request)

        path = re.sub("/+", "/", request.path)

        if response.status_code == 404:
            if not path.endswith('/'):
                request.path = path  # to force using the cleaned path
            else:
                request.path = path[:-1]  # to force using the cleaned path
            try:
                full_path = request.get_full_path(force_append_slash=True) # add the slash, keeping query parameters
                r = resolve(full_path)
                new_response = r.func(request, args=r.args, kwargs=r.kwargs)
                if new_response.status_code == 200:
                    return redirect(full_path)
            except Resolver404:
                pass  # this will fall through to `return response`

        # Add the Content-Length header to non-streaming responses if not
        # already set.
        if not response.streaming and not response.has_header('Content-Length'):
            response['Content-Length'] = str(len(response.content))
        return response

  1. 添加到项目的 ngnx 配置
    if ($request_uri ~* "\/\/") {
        rewrite ^/(.*)      $scheme://$host/    permanent;
    }
    # merge_slashes off;

它可以满足您的需要,而且 如果此页面存在,还会删除重复的斜杠。