【问题标题】:Python Django exception middleware for exception not in a view用于不在视图中的异常的 Python Django 异常中间件
【发布时间】:2016-08-01 21:59:02
【问题描述】:

每当在我的 django 应用程序中发现异常时,我想使用自定义中间件类来处理异常并发送电子邮件。

import logging
from django.core.mail import send_mail


class ErrorMiddleware(object):
    logger = logging.getLogger(__name__)

    def process_exception(self, request, exception):
        self.logger.debug("Middleware has caught an exception. exception={}".format(exception.message))

        # send_mail("Your Subject", "This is a simple text email body.",
        #           "Yamil Asusta <hello@yamilasusta.com>", ["yamil@sendgrid.com"])

    return None

我已将中间件添加到我的设置文件中,

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

    # custom middleware
    'buildconfig.middleware.ErrorMiddleware.ErrorMiddleware',
)

并编写了以下测试用例来测试中间件处理我的异常...

from django.test import TestCase
from mock import patch
from mock import Mock

class TestErrorMiddleware(TestCase):

    def test_process_exception_catches_exceptions(self):
        raise Exception

但是,当我通过 manage.py 运行测试时,我的日志中没有显示我的日志消息。

我错过了什么?

【问题讨论】:

  • 您是否正确设置了日志处理程序?
  • 但是...您的测试本身会引发异常。您没有在请求的上下文中运行它,那么中间件怎么可能捕获它?
  • 我希望它捕获所有异常,而不仅仅是视图中的请求发生的异常。
  • 但是中间件并不是魔法。它只能捕获中间件实际运行的上下文中发生的错误;也就是说,在请求中。

标签: python django exception-handling


【解决方案1】:

虽然来自@cdx530 的响应是正确的,但为 Django 1.10 之前设计的中间件有一个向后兼容的填充程序,作为 MIDDLEWARE_CLASSES 提供(几乎)与当代 MIDDLEWARE 方法相同的功能,并且恕我直言,提供了更好的结构和分离在不同的中间件处理阶段之间。

只需从 MiddlewareMixin 派生您的中间件类:

from django.middleware.common import MiddlewareMixin

class ErrorMiddleware(MiddlewareMixin):
    ....

除非你已经覆盖了 __init__ ,否则你不需要做任何其他事情。 __call__ 永远不会在这种情况下使用,这对于旧行为来说很好,并且 mixin 类将自动调用您的 process_request 和 process_response/processs_exception 之间的 get_request。

当前的 Django 文档在此处包含对此主题的讨论:

https://docs.djangoproject.com/en/3.1/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware

包括解释行为的细微差别。

【讨论】:

    【解决方案2】:

    一旦网络服务器启动,每个中间件都会被初始化。但要做到这一点,您需要在中间件中添加一个 __init__()。

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

    中间件初始化后,需要有一个 __call__() 方法,该方法会在客户端的每个请求上调用。正是这种方法负责将控制器传递给底层中间件或在最后一层即视图中间件之后调用实际视图。

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

    您需要从 process_exception() 返回一个 HTTPResponse(),它将在 response 变量中返回到中间件的 __call__()在上面的代码中,然后将其传递给它上面的中间件。

    由于您的中间件类中没有 __init__()__call__(),因此您的中间件没有被初始化,因此将每个请求作为其处理来回传递中间件层。

    参考: https://docs.djangoproject.com/en/2.0/topics/http/middleware/#init-get-response

    【讨论】:

      猜你喜欢
      • 2020-06-20
      • 2022-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-03
      • 2021-09-16
      相关资源
      最近更新 更多