Python Django异常中间件,不在视图中的异常

时间:2016-08-01 21:59:02

标签: python django exception-handling

每当我的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运行测试时,我看不到我的日志消息显示在我的日志中。

我错过了什么?

2 个答案:

答案 0 :(得分:1)

Web服务器启动后,将初始化所有中间件。但是要实现这一点,您需要在中间件中使用 __ 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

答案 1 :(得分:1)

虽然@cdx530 的响应是正确的,但有一个为 Django 1.10 之前的中间件设计的向后兼容性垫片,作为 MIDDLEWARE_CLASSES,它在当代 MIDDLEWARE 方法中提供(几乎)相同的功能,并且 imho 提供了更好的结构和分离在不同的中间件处理阶段之间。

只需从 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

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