Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django - why is this custom middleware not working

Tags:

django

I am building a website using django 1.10. I have written a middleware class to restrict the number of page views for users that are not logged in. I implemented this functionality into a separate application 'pagerestrict'

settings.py [Relevant Entries]

# 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 code (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'))

I tested this by going to my homepage and refreshing the browser several times - the code never seemed to be triggered. What am I missing?

like image 470
Homunculus Reticulli Avatar asked Jul 09 '17 14:07

Homunculus Reticulli


People also ask

Can we create custom middleware in Django?

Built-in Middleware are provided by default in Django when you create your project. You can check the default Middleware in settings.py file of your project. Custom Middleware — You can write your own middleware which can be used throughout your project.

How does middleware work in Django?

In Django, middleware is a lightweight plugin that processes during request and response execution. Middleware is used to perform a function in the application. The functions can be a security, session, csrf protection, authentication etc.

What is middleware What is important how do you write custom Middlewares in Django?

Middleware is a framework of hooks into Django's request/response processing. It's a light, low-level “plugin” system for globally altering Django's input or output. Each middleware component is responsible for doing some specific function.

Is middleware a decorator?

Middleware and decorators are similar and can do the same job. They provide a means of inserting intermediary effects either before or after other effects downstream in the chain/stack.


2 Answers

New-style middleware does not call process_request and process_response by default. This functionally has been replaced with __call__ and the get_response function that is passed to __init__.

You need to call process_request() inside __call__ and process the return value if it is not None. The easiest way to do this is to use the MiddlewareMixin provided by Django. This will define the necessary __init__ and __call__ methods, and __call__ will call process_request() and process_response() if they are defined.

from django.utils.deprecation import MiddlewareMixin


class CheckPageAccessMiddleware(MiddlewareMixin):
    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'))
like image 120
knbk Avatar answered Oct 12 '22 22:10

knbk


I finally got this to work. It was based on the Middleware topic on the now defunct Stack Overflow Documentation site. (Attribution details can be found on the contributor page. Reference topic 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
like image 35
Homunculus Reticulli Avatar answered Oct 13 '22 00:10

Homunculus Reticulli