首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Django源码阅读】Django 自定义异常处理页面源码解读

【Django源码阅读】Django 自定义异常处理页面源码解读

作者头像
the5fire
发布于 2019-08-12 07:21:48
发布于 2019-08-12 07:21:48
70400
代码可运行
举报
运行总次数:0
代码可运行

Django 自定义异常处理页面源码解读

这个解读来源于一个读者的反馈,于是花了几分钟看了下这部分源码,打算用十分钟的时间写一下,预计阅读需要 5 分钟。

自定义异常页面

Django 提供了常见的错误的页面,比如

  • 说用户访问了一个不存在的路径,引发的 404
  • 系统发生了一个异常,出现了 500

一个好的网站应该可以给用户友好的信息提示,比如:“服务器提了一个问题”之类的,然后给用户一个引导。对于商业网站需要注意的是错误页面的流量也是流量,应该有明确的引导。

在 Django 中定义这类处理很简单,只需要在 urls.py 中配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 参考:https://github.com/the5fire/typeidea/blob/deploy-to-cloud/typeidea/typeidea/urls.py#L24
handler404 = Handler404.as_view()
handler500 = Handler50x.as_view()

当然你需要定义这里面的 Handler50x:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class Handler404(CommonViewMixin, TemplateView):
    template_name = '404.html'

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context, status=404)


class Handler50x(CommonViewMixin, TemplateView):
    template_name = '50x.html'

    def get(self, request, *args, **kwargs):
        context = self.get_context_data(**kwargs)
        return self.render_to_response(context, status=500)

这样就可以简单的控制出错时展示给用户的页面了。需要注意的是,这个配置只会在非 Debug 模式下有效。

Django Error Handler 源码解析

要看这部分源码的第一步是判断 Django 可能会在哪处理这个异常。有很多方法,这里是说一种,从请求的入口开始撸。

注意我看到版本是 Django 2.0.1

1 WSGI Handler 的部分

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 代码:https://github.com/the5fire/django-inside/blob/84f272e1206554b43c86c0f7a50f37d1f3efbc28/django/core/handlers/wsgi.py#L135
class WSGIHandler(base.BaseHandler):
    request_class = WSGIRequest

    def __init__(self, *args, **kwargs):
        super(WSGIHandler, self).__init__(*args, **kwargs)
        self.load_middleware()

    def __call__(self, environ, start_response):
        set_script_prefix(get_script_name(environ))
        signals.request_started.send(sender=self.__class__, environ=environ)
        request = self.request_class(environ)
        response = self.get_response(request)  # the5fire: 注意这儿
        # ... the5fire:省略其他

2 BaseHandler 中的 get_response

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    # ref: https://github.com/the5fire/django-inside/blob/84f272e1206554b43c86c0f7a50f37d1f3efbc28/django/core/handlers/base.py#L94
    def get_response(self, request):
        """Return an HttpResponse object for the given HttpRequest."""
        # Setup default url resolver for this thread
        set_urlconf(settings.ROOT_URLCONF)

        response = self._middleware_chain(request)  # the5fire: 这里进去

        response._closable_objects.append(request)

        # If the exception handler returns a TemplateResponse that has not
        # been rendered, force it to be rendered.
        if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
            response = response.render()

        if response.status_code == 404:
            logger.warning(
                'Not Found: %s', request.path,
                extra={'status_code': 404, 'request': request},
            )

        return response

3 被包装的 _middleware_chain

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    # https://github.com/the5fire/django-inside/blob/84f272e1206554b43c86c0f7a50f37d1f3efbc28/django/core/handlers/base.py#L76
    def load_middleware(self):
        """
        Populate middleware lists from settings.MIDDLEWARE.

        Must be called after the environment is fixed (see __call__ in subclasses).
        """
        self._request_middleware = []
        self._view_middleware = []
        self._template_response_middleware = []
        self._response_middleware = []
        self._exception_middleware = []

        handler = convert_exception_to_response(self._get_response)
        for middleware_path in reversed(settings.MIDDLEWARE):
            middleware = import_string(middleware_path)
            # ...  the5fire:忽略中间这些代码
            handler = convert_exception_to_response(mw_instance)

        # We only assign to this when initialization is complete as it is used
        # as a flag for initialization being complete.
        self._middleware_chain = handler

4 具体处理异常的部分

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    def convert_exception_to_response(get_response):
        """
        Wrap the given get_response callable in exception-to-response conversion.

        All exceptions will be converted. All known 4xx exceptions (Http404,
        PermissionDenied, MultiPartParserError, SuspiciousOperation) will be
        converted to the appropriate response, and all other exceptions will be
        converted to 500 responses.

        This decorator is automatically applied to all middleware to ensure that
        no middleware leaks an exception and that the next middleware in the stack
        can rely on getting a response instead of an exception.
        """
        @wraps(get_response)
        def inner(request):
            try:
                response = get_response(request)
            except Exception as exc:
                response = response_for_exception(request, exc)  # the5fire: 这里进去
            return response
        return inner


    def response_for_exception(request, exc):
        if isinstance(exc, Http404):
            if settings.DEBUG:
                response = debug.technical_404_response(request, exc)
            else:
                response = get_exception_response(request, get_resolver(get_urlconf()), 404, exc)

        # ... the5fire: 省略掉一大坨类似的代码

        else:
            signals.got_request_exception.send(sender=None, request=request)
            # the5fire: 下面这一行,具体的处理逻辑。
            response = handle_uncaught_exception(request, get_resolver(get_urlconf()), sys.exc_info())

        # Force a TemplateResponse to be rendered.
        if not getattr(response, 'is_rendered', True) and callable(getattr(response, 'render', None)):
            response = response.render()

        return response

5 异常处理逻辑

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    # https://github.com/the5fire/django-inside/blob/84f272e1206554b43c86c0f7a50f37d1f3efbc28/django/core/handlers/exception.py#L107
    def handle_uncaught_exception(request, resolver, exc_info):
        """
        Processing for any otherwise uncaught exceptions (those that will
        generate HTTP 500 responses).
        """
        if settings.DEBUG_PROPAGATE_EXCEPTIONS:
            raise

        logger.error(
            'Internal Server Error: %s', request.path,
            exc_info=exc_info,
            extra={'status_code': 500, 'request': request},
        )

        if settings.DEBUG:
            return debug.technical_500_response(request, *exc_info)

        # Return an HttpResponse that displays a friendly error message.
        # the5fire: 这里会解析到对应的handler ,比如我们定义的那个
        callback, param_dict = resolver.resolve_error_handler(500)
        return callback(request, **param_dict)

6 最终解析到 urls/resolvers.py 中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    # 完整代码: https://github.com/the5fire/django-inside/blob/84f272e1206554b43c86c0f7a50f37d1f3efbc28/django/urls/resolvers.py#L555
    def resolve_error_handler(self, view_type):
        callback = getattr(self.urlconf_module, 'handler%s' % view_type, None)  # the5fire: 这里就是去获取 urls.py 中对应的配置
        if not callback:
            # No handler specified in file; use lazy import, since
            # django.conf.urls imports this file.
            from django.conf import urls
            callback = getattr(urls, 'handler%s' % view_type)
        return get_callable(callback), {}

最后

实际上花了比预计更多的时间来把完整的代码贴出来,以及明确对应的版本。在 Django 1.11 中的处理逻辑有些不同。

实际阅读时间也会比预计的久,但如果能理解这个过程,你对于Django也会有更深的进步。

- from the5fire.com

----EOF----- 微信公众号:Python程序员杂谈

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-08-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
云服务器待解决问题
迁移,升级都没问题,但是一些版本冲突和想增加搜索引擎的问题一直没解决,先记录下,后续有时间再继续吧;
古道无仙
2022/03/04
7560
云服务器待解决问题
“加密系统”的巨坑
SAP梦心
2018/01/05
7K1
“加密系统”的巨坑
精灵之息的玩法
而且很多功能似乎来来回回的改了好几遍(头一天添加新功能,第二天觉得这个功能不行,删掉,过了一阵子又添加回来)的情况。
沙因Sign
2019/08/09
5630
精灵之息的玩法
心诺安 x TapData:快速搭建云中数仓,助力电商企业实施“以用户为中心的”精细化运营
新一轮“618”大促火热进行中。此前,各大电商平台纷纷宣布,今年将取消往年的预售模式,改为直接开售配合官方补贴的策略。外部多将这一变化解读为行业顺应市场呼唤、积极创新的结果。
Tapdata
2024/06/07
1700
心诺安 x TapData:快速搭建云中数仓,助力电商企业实施“以用户为中心的”精细化运营
低代码指南100问:42国外低代码平台一览表?
微软团队开发的一款SaaS产品,提供应用程序开发环境,协助无代码快速自定义应用开发;同时提供开发扩展功能,专业的技术开发人员可创建数据和元数据,实现自主开发,扩展应用逻辑、创建自定义连接器或实现数据集成。
LCHub低代码社区
2023/06/19
7040
低代码指南100问:42国外低代码平台一览表?
【KPaaS洞察】一文读懂什么是“多源数据集成”
如今,数据已成为企业最宝贵的资产。然而,现实情况往往是,数据并非存储在一个地方,而是散落在企业内部的各个系统和外部平台中。从CRM、ERP到各类业务系统,再到物联网设备和社交媒体,数据来源的多样性使得企业面临着巨大的挑战——如何将这些分散的数据有效地整合起来,形成统一的视图,从而支撑业务决策和创新? 这正是“多源数据集成”所要解决的核心问题。
KPaaS集成扩展
2025/07/30
1320
【KPaaS洞察】一文读懂什么是“多源数据集成”
[PowerBI]中国首款重量级PowerBIDeskTop外部工具问世
过往的PowerBIDeskTop,它是一个独立的软件,不像Excel那样可以有二次开发的接口,但7月份更新PowerBIDeskTop后,已经开放了外部工具,单独有一个外部工具的选项卡。
Excel催化剂
2021/08/18
4.3K0
[PBI催化剂]国际水准,中国首款重量级PowerBIDeskTop外部工具问世
SQLBI的工具,有兴趣的可了解下,需要点英文阅读能力:https://www.sqlbi.com/tools/analyze-in-excel-for-power-bi-desktop/
Excel催化剂
2021/08/18
3.3K0
全球首发,PBI催化剂更新,PowerBI参数字段表再爱多一点,批量创建,纯界面零代码,Excel数据源一键生成。
除此之外,另增加批量创建表关系功能,对同类事实表追加关系变得SoEasyShu,又是批量完成,爽到爆。
Excel催化剂
2023/04/26
2.3K0
全球首发,PBI催化剂更新,PowerBI参数字段表再爱多一点,批量创建,纯界面零代码,Excel数据源一键生成。
云时代下的服务器选择:虚拟主机、VPS与云服务器深度分析
gavin1024
2025/07/29
1040
如何巧妙的使用Power BI计算同比增长
小SUN目前就职于一家葡萄酒分销公司,其主要职责就是为业务部门提供数据分析报告,其中一份报告是追踪销售团队的KPI并与去年同期进行对比。
公众号PowerBI大师
2019/11/12
8.6K0
如何巧妙的使用Power BI计算同比增长
王者回归,PBI催化剂2.0发布,回归初心,个人永久性免费使用全部功能
PBI催化剂是笔者两年前开发的国内首款PowerBI外部工具,用于在PowerBI模型和报表层的元数据批量管理。
Excel催化剂
2022/09/02
5K0
王者回归,PBI催化剂2.0发布,回归初心,个人永久性免费使用全部功能
云服务器的N种玩法,拥有腾讯云服务器构建你的数字世界
每年一度的双十一购物节,不仅是电商平台的狂欢盛宴,更是云计算行业的“黄金时段”。作为国内领先的云服务提供商之一,腾讯云每年都会推出一系列丰富的优惠活动,让企业和个人用户可以以更加实惠的价格,享受到云服务带来的强大能力。
星哥玩云
2024/11/13
6250
云服务器的N种玩法,拥有腾讯云服务器构建你的数字世界
3款口碑炸裂的BI数据分析工具测评
大家听得最多的莫过于Tableau、微软的Power BI,还有国产的FineBI。
物流IT圈
2019/07/16
5.3K0
3款口碑炸裂的BI数据分析工具测评
异构系统数据集成之数据源管理:打通企业数据孤岛的关键一步
企业内部的业务系统日益复杂,从传统的ERP、CRM、MES到新兴的微服务架构应用、云端SaaS平台,数据以多种形态分布在不同的技术栈和存储介质中。这些“异构系统”虽然支撑着企业的核心业务运转,却也带来了严重的“数据孤岛”问题——数据分散、标准不一、访问困难,严重制约了数据分析的时效性与决策的科学性。
KPaaS集成扩展
2025/08/07
970
异构系统数据集成之数据源管理:打通企业数据孤岛的关键一步
哪种云服务器适合小型企业使用?
云服务器的操作简单、低成本的特点成为了小型企业选择服务器的首选。它支持随时随地过任何移动设备获得访问权限。云计算为小型企业提供了以前不敢相信的技术,并让他们可以与大型企业有竞争的机会。
用户1097350
2018/06/12
3.4K0
哪种云服务器适合小型企业使用?
如何设计一个软件项目管理系统:架构设计合集(六)
想象一下这个场景:项目经理小王每天都在各种群里询问进度,开发小李在纸质便签上记录任务,测试小张用Excel表格跟踪Bug… 这种"原始"的管理方式不仅效率低下,还容易出错。
蓝葛亮
2025/07/20
1180
如何设计一个软件项目管理系统:架构设计合集(六)
什么是数据集成平台?数据集成平台有哪些功能?
如今,企业每天的数据量真是大得惊人,而且这些数据来源五花八门,来自不同系统、不同格式的数据都散落在各处。想把它们互通和整合起来,那可真是费老大劲了!这时候,数据集成平台的作用就明明白白地显示出来了。简单来说,数据集成平台就是一种专门用来收集、整合和管理来自不同源头的数据的工具。
帆软BI
2025/07/02
1310
什么是数据集成平台?数据集成平台有哪些功能?
Tableau/PowerBI的“割裂”和帆软BI的“集成”
上周,一位Tableau客户辗转发来“问题”,说帆软销售给他们发了一个功能对比清单,大致如下:
Tableau喜乐君
2024/11/25
5210
Tableau/PowerBI的“割裂”和帆软BI的“集成”
为什么连外包公司都对你爱答不理?
随着互联网+口号和政策的推动,很多企业,尤其是传统企业都在转型,也越来越认识到互联网的重要性,都在想办法如何切入互联网领域,无论是切入互联网是为了分一杯羹,还是利用互联网提高公司的管理和效率,都是为了走进政府提倡的互联网+之中。 甲方:我们公司是传统国企,有大量的工人,我们想做个答题软件,答题送红包的模式,每天鼓励公司员工进行答题,学习安全管理,安全施工方面的知识。非常简单的,就有在线练习,模拟答题,正式答题,错题库,还有升级规则以及赠送红包,抽奖的几率就行。另外,题库里要分模块,因为有很多工种,答题也不
非著名程序员
2018/07/19
6620
推荐阅读
相关推荐
云服务器待解决问题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档