django - 为什么这个自定义中间件无法正常工作

时间:2017-07-09 14:21:18

标签: django

我正在使用django 1.10构建一个网站。我编写了一个中间件类来限制未登录用户的页面查看次数。我将此功能实现到一个单独的应用程序' pagerestrict'

settings.py [相关条目]

# Application definition

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',

        'django.contrib.sites',

        'pagerestrict.apps.PagerestrictConfig',
        # ...
    ]

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware', 

        'pagerestrict.middleware.CheckPageAccessMiddleware',    
    ]

中间件代码(middleware.py):

from django.http import HttpResponseRedirect
from decouple import config

class CheckPageAccessMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        return self.get_response(request)

    def process_request(self, request):
        if not request.user.is_authenticated():
            current_page_url = request.path_info.lstrip('/')

            ignore_list = config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])

            #if not current_page_url or not any(current_page_url != url for url in ignore_list):
            if not current_page_url in ignore_list:
                # first things first ...
                if not has_attr(request, 'pages_visited'):
                    request.pages_visited = set()

                if not has_attr(request, 'page_count'):
                    request.page_count = 0

                if current_page_url not in request.pages_visited:
                    request.pages_visited.add(current_page_url)

                request.page_count += 1

                if request.page_count > config('PAGE_RESTRICT_PAGE_LIMIT'):
                    return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))

我通过访问我的主页并多次刷新浏览器来测试这一点 - 代码似乎永远不会被触发。我错过了什么?

5 个答案:

答案 0 :(得分:8)

默认情况下,新式中间件不会调用process_requestprocess_response。此功能已替换为__call__和传递给get_response的{​​{1}}函数。

您需要在__init__内调用process_request()并处理返回值,如果它不是__call__。最简单的方法是使用Django提供的None。这将定义必要的MiddlewareMixin__init__方法,__call__将调用__call__process_request()(如果已定义)。

process_response()

答案 1 :(得分:4)

我终于开始工作了。它基于现已解散的Stack Overflow Documentation站点上的Middleware主题。 (可以在contributor page上找到归因详细信息。参考主题ID:1721。)

from django.http import HttpResponseRedirect
from decouple import config

class CheckPageAccessMiddleware(object):
    def __init__(self, next_layer=None):
        """We allow next_layer to be None because old-style middlewares
        won't accept any argument.
        """
        self.get_response = next_layer

    def process_request(self, request):
        """Let's handle old-style request processing here, as usual."""
        # Do something with request
        # Probably return None
        # Or return an HttpResponse in some cases
        if not request.user.is_authenticated():
            current_page_url = request.path_info.lstrip('/')
            print ('Current page url: {0}'.format(current_page_url))

            root_url = '' # root url == ('')
            restrict_redirect_page = config('PAGE_RESTRICT_REDIRECT_URL')[1:]  # chop of leading '/'
            ignore_list = [root_url, restrict_redirect_page] + \
                config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])
            print('ignore list: %s' % ",".join([x for x in ignore_list]))

            #if not current_page_url or not any(current_page_url != url for url in ignore_list):
            if not current_page_url in ignore_list:
                # first things first ...
                if 'pages_visited' not in request.session:
                    request.session['pages_visited'] = ''

                if 'page_count' not in request.session:
                    request.session['page_count'] = 0

                pages_visited = [x.strip() for x in request.session['pages_visited'].split(',')]
                if current_page_url not in pages_visited:
                    pages_visited.append(current_page_url)
                    request.session['pages_visited'] = ",".join(x.strip() for x in pages_visited)

                request.session['page_count'] += 1

                print('Request page count: {0}'.format(request.session['page_count']))

                if request.session['page_count'] > config('PAGE_RESTRICT_PAGE_LIMIT', cast=int):
                    return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL')) 

        return None


    def process_response(self, request, response):
        """Let's handle old-style response processing here, as usual."""
        # Do something with response, possibly using request.
        return response


    def __call__(self, request):
        """Handle new-style middleware here."""
        response = self.process_request(request)
        if response is None:
            # If process_request returned None, we must call the next middleware or
            # the view. Note that here, we are sure that self.get_response is not
            # None because this method is executed only in new-style middlewares.
            response = self.get_response(request)

        response = self.process_response(request, response)
        return response

答案 2 :(得分:1)

您可以像这样更改导入:

try:
    from django.utils.deprecation import MiddlewareMixin
except ImportError:
    MiddlewareMixin = object

答案 3 :(得分:0)

新型中间件默认情况下不会调用process_request和process_response。 此功能已由 call 和get_response函数取代,  传递给 init 。N您需要在调用中调用process_request(),然后 如果返回值不是None,则处理返回值。 因此,您可以将要放入process_request()方法中的代码编写为调用(),如下所示:

from django.http import HttpResponseRedirect
from decouple import config

class CheckPageAccessMiddleware(object):
    def __init__(self, next_layer=None):
             self.get_response = next_layer

    def __call__(self, request):
         if not request.user.is_authenticated():
            current_page_url = request.path_info.lstrip('/')

            ignore_list = config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])

            #if not current_page_url or not any(current_page_url != url for url in ignore_list):
            if not current_page_url in ignore_list:
                # first things first ...
                if not has_attr(request, 'pages_visited'):
                    request.pages_visited = set()

                if not has_attr(request, 'page_count'):
                    request.page_count = 0

                if current_page_url not in request.pages_visited:
                    request.pages_visited.add(current_page_url)

                request.page_count += 1

                if request.page_count > config('PAGE_RESTRICT_PAGE_LIMIT'):
                    return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))

答案 4 :(得分:0)

  • 这里是一个示例,展示了如何在迁移到中间件时更新中间件
    Django ver > 1.10Upgrading pre-Django 1.10-style middleware

  • 如果它不起作用:
    您还应该暂时删除所有default MIDDLEWARE class
    仅保留your custom MIDDLEWARE以确保前一个 层(default MIDDLEWARE)并未中断流程:

MIDDLEWARE = (
    # 'django.middleware.common.CommonMiddleware',
    'app.middleware.MyMiddlewareClass',
)
  • 如果您删除django.middleware.common.CommonMiddleware,它将起作用。那可能是ALLOWED_HOSTS问题。
- Replace
ALLOWED_HOSTS= ['localhost:8001']
- With
ALLOWED_HOSTS= ['localhost']