如何在Django中设置自定义中间件?

113
我正在尝试创建一个中间件,以便在满足条件的每个视图中可选择地传递一个关键字参数。
问题是我找不到如何设置中间件的示例。我看到了一些覆盖我想要的方法process_view的类。
Class CheckConditionMiddleware(object):  
    def process_view(self, request):  
          
        return None  

但是我应该把这个类放在哪里呢?我要创建一个中间件应用程序,然后把这个类放在其中,并在settings.middleware中引用它吗?


2
你可以在这里跟随我的一个答案:https://dev59.com/vmMm5IYBdhLWcg3wRdMz#17777539 - Hieu Nguyen
虽然那是一个很好的答案,但你也可以参考 Django 书籍 - karthikr
你可以这样做:https://dev59.com/vmMm5IYBdhLWcg3wRdMz#17777539 - Atma
@karthikr,你提供的链接已经失效了 :/ - fedorqui
6个回答

182

首先:路径结构

如果你没有它,你需要在你的应用程序中创建middleware文件夹,并按照以下结构进行:

yourproject/yourapp/middleware

文件夹中间件应该放置在与settings.py、urls和templates相同的文件夹中...

重要提示:不要忘记在middleware文件夹中创建init.py空文件,以便您的应用程序识别此文件夹

第二步:创建中间件

现在我们应该为自定义中间件创建一个文件,在本例中假设我们想要一个基于IP过滤用户的中间件,我们在middleware文件夹中创建名为filter_ip_middleware.py的文件,并添加以下代码:

class FilterIPMiddleware(object):
    # Check if client IP is allowed
    def process_request(self, request):
        allowed_ips = ['192.168.1.1', '123.123.123.123', etc...] # Authorized ip's
        ip = request.META.get('REMOTE_ADDR') # Get client IP
        if ip not in allowed_ips:
            raise Http403 # If user is not allowed raise Error
 
       # If IP is allowed we don't do anything
       return None

第三步:在我们的 'settings.py' 中添加中间件

我们需要查找以下内容:

  • MIDDLEWARE_CLASSES (django < 1.10)
  • MIDDLEWARE (django >= 1.10)

在 settings.py 中,我们需要添加我们的中间件(将其添加到最后一个位置)。代码应该如下所示:

MIDDLEWARE = ( #  Before Django 1.10 the setting name was 'MIDDLEWARE_CLASSES'
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
     # Above are django standard middlewares

     # Now we add here our custom middleware
     'yourapp.middleware.filter_ip_middleware.FilterIPMiddleware'
)

完成! 现在每个客户端的请求都将调用您的自定义中间件并处理您的自定义代码!


文件路径正确吗?难道不应该是yourproject/yourapp/middleware吗? - tiagovrtr
是的 @tiagovrtr,路径应该是 yourproject/yourapp/middleware。我认为这很明显,但如果这能让你更清楚,我会进行更新。 - AlvaroAV
19
如果遇到“TypeError:object()不接受参数”的情况,请查看此答案 - T. Christiansen
1
此答案中添加了一个更近期的Django版本的示例(以及对TypeError:object() takes no parameters的修复)。 - Roald Nefs
我不知道...我花了很多时间,头疼,眼睛想睡觉,我无法创建问题,因为我被禁止了。我按照你说的尝试了所有方法,但它告诉我“django.core.exceptions.ImproperlyConfigured: WSGI application”。所以,我认为我需要安装自定义中间件,但没有人谈论它... :( - Sergo

21

编写Django>=1.10中间件

自Django 1.10版本以来,中间件类必须在其__init__()方法中接受一个get_response参数并提供一个__call__()方法。虽然可以通过在定义中间件类时使用django.utils.deprecation.MiddlewareMixin来实现这一点(如W.Perrin的答案所示),但是在当前支持的Django版本中创建基于类的中间件看起来像这样:

class CustomMiddleware(object):
    def __init__(self, get_response):
        """
        One-time configuration and initialisation.
        """
        self.get_response = get_response

    def __call__(self, request):
        """
        Code to be executed for each request before the view (and later
        middleware) are called.
        """
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        """
        Called just before Django calls the view.
        """
        return None

    def process_exception(self, request, exception):
        """
        Called when a view raises an exception.
        """
        return None

    def process_template_response(self, request, response):
        """
        Called just after the view has finished executing.
        """
        return response

process_view()process_exception()process_template_response()是特殊的钩子函数,在处理中间件时Django会调用它们,你可以在自己的中间件类中定义。在上面的示例中,实现的钩子函数除了确保Django调用下一个中间件进一步处理响应/请求外,不会有其他特殊操作。

激活中间件

要激活中间件组件,请将其添加到Django设置中的MIDDLEWARE列表中。

MIDDLEWARE = [
    # Default Django 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',

    # Add your custom middleware
    'path.to.your.middleware.CustomMiddleware',
]


在设置中的路径可以更加具体,例如 appname.middleware.middlewarefilename.CustomMiddleware - sid_mac

7

只需要两个步骤。对于 django2.1 我使用以下方法解决了此问题。

1.创建自己的中间件类。

官方手册有很好的演示。

https://docs.djangoproject.com/en/2.1/ref/request-response/#django.http.HttpRequest.get_host

    from django.utils.deprecation import MiddlewareMixin

    class MultipleProxyMiddleware(MiddlewareMixin):
        FORWARDED_FOR_FIELDS = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_FORWARDED_HOST',
            'HTTP_X_FORWARDED_SERVER',
        ]

        def process_request(self, request):
            """
            Rewrites the proxy headers so that only the most
            recent proxy is used.
            """
            for field in self.FORWARDED_FOR_FIELDS:
                if field in request.META:
                    if ',' in request.META[field]:
                        parts = request.META[field].split(',')
                        request.META[field] = parts[-1].strip()

2. 在项目的 setting.py 文件的 MIDDLEWARE 列表中引用您的 Middleware 类。

Middleware 引用的规则是从项目的根目录到您的类的路径。

例如,在名为 mysite 的项目中,树形结构如下。

├── mysite
│   ├── manage.py
│   ├── mysite
│   │   ├── __init__.py
│   │   ├── middleware.py
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py

我们刚刚在middleware.py文件中添加了我们的中间件类MultipleProxyMiddleware。我们得到以下参考名称。
MIDDLEWARE = [
    'mysite.middleware.MultipleProxyMiddleware',  
     ...
]

尝试了但是出现了...模块未找到错误:没有名为'mysite.mymiddleware'的模块。 - Mike Oliver

0
例如,按照下面的示例,在core/中创建一个名为middleware的文件夹,并包含__init__.py(空文件)和custom.py。*我参考了文档,并使用了Django 4.2.3版本:
django-project
 |-core
 |  |-settings.py
 |  └-middleware # Here
 |     |-__init__.py
 |     └-custom.py
 |-my_app1
 └-my_app2

然后,将以下代码放入custom.py中:
# "core/middleware/custom.py"

def simple_middleware(get_response):
    print("Here is run only once when the server starts")

    def middleware(request):

        print("Here is run before a view is run")

        response = get_response(request)

        print("Here is run after a view is run")

        return response

    return middleware

class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        print("Here is run only once when the server starts")

    def __call__(self, request):

        print("Here is run before a view is run")

        response = self.get_response(request)

        print("Here is run after a view is run")

        return response

最后,将simple_middlewareSimpleMiddleware设置为MIDDLEWARE,然后每次加载Django网站时,都会运行您设置的中间件MIDDLEWARE

# "core/settings.py"

...

MIDDLEWARE = [
    ...
    'core.middleware.custom.simple_middleware', # Here
    'core.middleware.custom.SimpleMiddleware' # Here
]

...

0
首先,中间件实际上是Httprequest和HttpResponse之间的桥梁,通常是全局的,因为它是桥梁,因为HttpRequest必须通过桥梁到达服务器并通过HttpResponse将桥梁返回客户端。 它非常酷,这意味着您可以编写一堆方法在请求到达服务器之前或之后运行。 以csrfmiddleware为例,请求将首先由中间件判断其是否为POST方法,如果为真,则中间件将比较其所拥有的csrf_token与存储在服务器内部的令牌,该令牌是在发送带有表单标记的HTML时生成的,因为通常情况下,客户端只能通过表单服务器直接发送POST请求,因此服务器可以使用此来判断此POST是否来自表单服务器发送给您,并结合身份验证或授权,决定是否将请求发送到服务器或仅拒绝请求。 因此,当您编写自己的中间件时,请明确您想要对请求或响应执行的操作,您是否想在响应中添加一个元素?就像messagemiddleware所做的那样,这个新元素可以被视为django视图发送的上下文或者您想添加会话,并在每次客户端发出请求时检查会话,以此为思路,遵循一些固定格式,就像在这个网站中所示https://medium.com/scalereal/everything-you-need-to-know-about-middleware-in-django-2a3bd3853cd6

-2

当您知道视图中发生了哪种类型的异常时,这将非常有帮助。

基于上述情况,我创建了自己的自定义类,如下所示:

from .models import userDetails

class customMiddleware(object):

    def process_request(self,request):
        result=''
        users = userDetails.objects.all()
        print '-->',users ,'---From middleware calling ---'

        username=request.POST.get("username")
        salary = request.POST.get("salary")
        if salary:
            try:
                result = username+int(salary)
            except:
                print "Can't add"

当字符串和整数相加时发生异常时,它将被执行。

您可以为上述中间件类编写相应的视图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接