• Flask 学习69.捕获异常钩子函数errorhandler 上海


    前言

    flask 运行请求出现异常时,会先触发对应的异常钩子,比如出现404时,会根据NotFound 异常类返回404状态码。
    我们也可以根据捕获异常钩子errorhandler 来自定义异常的输出。

    404 NotFound

    以404 NotFound 为例,在werkzeug.exceptions中可以找到

    class NotFound(HTTPException):
        """*404* `Not Found`
    
        Raise if a resource does not exist and never existed.
        """
    
        code = 404
        description = (
            "The requested URL was not found on the server. If you entered"
            " the URL manually please check your spelling and try again."
        )
    

    NotFound 类继承了一个基类HTTPException

    class HTTPException(Exception):
        """The base class for all HTTP exceptions. This exception can be called as a WSGI
        application to render a default error page or you can catch the subclasses
        of it independently and render nicer error messages.
        """
    
        code: t.Optional[int] = None
        description: t.Optional[str] = None
    

    我们只需要继承HTTPException类,自定义code和description就可以指定不同状态码返回了。

    当我们访问一个不存在的地址,先抛出NotFound异常,然后触发异常钩子,返回对应的code和description

    于是我们可以自定义这个404 页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>404</title>
    </head>
    <body>
       <h1>404 page object not found !</h1>
    </body>
    </html>
    

    errorhandler 使用

    errorhandler 相关源码

        @setupmethod
        def errorhandler(
            self, code_or_exception: t.Union[t.Type[Exception], int]
        ) -> t.Callable[[ErrorHandlerCallable], ErrorHandlerCallable]:
            """Register a function to handle errors by code or exception class.
    
            A decorator that is used to register a function given an
            error code.  Example::
    
                @app.errorhandler(404)
                def page_not_found(error):
                    return 'This page does not exist', 404
    
            You can also register handlers for arbitrary exceptions::
    
                @app.errorhandler(DatabaseError)
                def special_exception_handler(error):
                    return 'Database connection failed', 500
    

    通过使用 errorhandler() 装饰函数来注册或者使用 register_error_handler() 来注册

    @app.errorhandler(werkzeug.exceptions.BadRequest)
    def handle_bad_request(e):
        return 'bad request!', 400
    
    # or, without the decorator
    app.register_error_handler(400, handle_bad_request)
    

    使用@app.errorhandler() 装饰器返回自定义的页面

    from flask import Flask, request, g, abort, Response, render_template
    app = Flask(__name__)
    
    
    @app.errorhandler(404)
    def error_404(error):
        return render_template('404.html'), 404
    
    
    @app.route("/demo", methods=["GET"])
    def demo():
    
        return {'msg': 'ok'}
    
    
    if __name__ == '__main__':
        app.run()
    

    使用的时候需注意三点
    1.errorhandler()括号里面传对应状态码或者一个异常类
    2.函数error_404(error) 括号里面必须传一个位置参数接收异常
    3.return 返回的时候需带上状态码(404),没带上状态码默认返回200

    errorhandler 传异常类示例

    werkzeug.exceptions导入异常类

    from werkzeug.exceptions import NotFound
    
    
    @app.errorhandler(NotFound)
    def error_404(error):
        return render_template('404.html'), 404
    

    效果和上面传404 参数一样

    自定义400 bad request

    请求参数不合法时,我们一般会返回400 bad request, 默认返回的是一个html页面

    在开发接口的时候,我们希望统一返回json 格式

    @app.errorhandler(400)
    def error_400(error):
        return {'msg': '请求参数不合法', 'data': f'{error}'}, 400
    
    
    @app.route("/demo", methods=["GET"])
    def demo():
        if request.args.get('username'):
            abort(400)
        return {'msg': 'ok'}
    

    当访问一个不存在的地址时,就会返回400的json格式

    HTTP/1.0 400 BAD REQUEST
    Content-Type: application/json
    Content-Length: 145
    Server: Werkzeug/2.0.1 Python/3.8.5
    Date: Sun, 11 Sep 2022 14:19:53 GMT
    
    {
      "data": "400 Bad Request: The browser (or proxy) sent a request that this server could not understand.", 
      "msg": "请求参数不合法"
    }
    

    处理

    在处理请求时,当 Flask 捕捉到一个异常时,它首先根据代码检索。如果该代码没 有注册处理器,它会根据类的继承来查找,确定最合适的注册处理器。如果找不到已 注册的处理器,那么 HTTPException 子类会显示 一个关于代码的通用消息。没有代码的异常会被转化为一个通用的 500 内部服务器 错误。

    例如,如果一个 ConnectionRefusedError 的实例被抛出,并且一个出错处 理器注册到 ConnectionError 和 ConnectionRefusedError ,那么 会使用更合适的 ConnectionRefusedError 来处理异常实例,生成响应。

    当一个蓝图在处理抛出异常的请求时,在蓝图中注册的出错处理器优先于在应用中全 局注册的出错处理器。
    但是,蓝图无法处理 404 路由错误,因为 404 发生的路由级 别还不能检测到蓝图。

    通用异常处理器

    可以为非常通用的基类注册异常处理器,例如 HTTPException 基类或者甚至 Exception 基类。但是,请注意,这样会捕捉到超出你预期的异常。

    基于 HTTPException 的异常处理器对于把缺省的 HTML 出错页面转换为 JSON 非常有用,但是这个处理器会触发不由你直接产生的东西,
    如路由过程中产生的 404 和 405 错误。请仔细制作你的处理器,确保不会丢失关于 HTTP 错误的信息。

    from flask import json
    from werkzeug.exceptions import HTTPException
    
    @app.errorhandler(HTTPException)
    def handle_exception(e):
        """Return JSON instead of HTML for HTTP errors."""
        # start with the correct headers and status code from the error
        response = e.get_response()
        # replace the body with JSON
        response.data = json.dumps({
            "code": e.code,
            "name": e.name,
            "description": e.description,
        })
        response.content_type = "application/json"
        return response
    

    基于 Exception 的异常处理器有助于改变所有异常处理的表现形式,甚至包含 未处理的异常。但是,与在 Python 使用 except Exception: 类似,这样会捕 获 所有 未处理的异常,包括所有 HTTP 状态码。因此,在大多数情况下,设定 只针对特定异常的处理器比较安全。 因为 HTTPException 实例是一个合法的 WSGI 响应,你可以直接传递该实例。

    from werkzeug.exceptions import HTTPException
    
    @app.errorhandler(Exception)
    def handle_exception(e):
        # pass through HTTP errors
        if isinstance(e, HTTPException):
            return e
    
        # now you're handling non-HTTP exceptions only
        return render_template("500_generic.html", e=e), 500
    

    异常处理器仍然遵循异常烦类的继承层次。如果同时基于 HTTPException 和 Exception 注册了异常处理器, Exception 处理器不会处理 HTTPException 子类,因为 HTTPException 更有针对性。

    未处理的异常 500

    当一个异常发生时,如果没有对应的异常处理器,那么就会返回一个 500 内部服务错误。关于此行为的更多内容参见 flask.Flask.handle_exception() 。

    如果针为 InternalServerError 注册了异常处理器,那么出现内部服务错误时就 会调用这个处理器。自 Flask 1.1.0 开始,总是会传递一个 InternalServerError 实例给这个异常处理器,而不是以前的未处理异常。原始 的异常可以通过 e.original_error 访问。在 Werkzeug 1.0.0 以前,这个属性 只有未处理异常有。建议使用 getattr 访问这个属性,以保证兼容性。

    @app.errorhandler(InternalServerError)
    def handle_500(e):
        original = getattr(e, "original_exception", None)
    
        if original is None:
            # direct 500 error, such as abort(500)
            return render_template("500.html"), 500
    
        # wrapped unhandled error
        return render_template("500_unhandled.html", e=original), 500
    
  • 相关阅读:
    awk
    django教材
    saltstack的安装过程
    OPENSTACK学习笔记(1)
    5G核心网架构
    内存采集
    分析CPU文件
    环境管理系统
    属性的两种定义方式
    Python 面向对象(初级篇)
  • 原文地址:https://www.cnblogs.com/yoyoketang/p/16685011.html
Copyright © 2020-2023  润新知