第三章:定制异常

1.自定义异常对象

flask所有的异常都继承自 HTTPException,例如werkzeug.exceptions下

class BadRequest(HTTPException):

    """*400* `Bad Request`

    Raise if the browser sends something to the application the application
    or server cannot handle.
    """
    code = 400
    description = (
        'The browser (or proxy) sent a request that this server could '
        'not understand.'
    )


class ClientDisconnected(BadRequest):

    """Internal exception that is raised if Werkzeug detects a disconnected
    client.  Since the client is already gone at that point attempting to
    send the error message to the client might not work and might ultimately
    result in another exception in the server.  Mainly this is here so that
    it is silenced by default as far as Werkzeug is concerned.

    Since disconnections cannot be reliably detected and are unspecified
    by WSGI to a large extent this might or might not be raised if a client
    is gone.

    .. versionadded:: 0.8
    """


class SecurityError(BadRequest):

    """Raised if something triggers a security error.  This is otherwise
    exactly like a bad request error.

    .. versionadded:: 0.9
    """


class BadHost(BadRequest):

    """Raised if the submitted host is badly formatted.

    .. versionadded:: 0.11.2
    """


class Unauthorized(HTTPException):

    """*401* `Unauthorized`

    Raise if the user is not authorized.  Also used if you want to use HTTP
    basic auth.
    """
    code = 401
    description = (
        'The server could not verify that you are authorized to access '
        'the URL requested.  You either supplied the wrong credentials (e.g. '
        'a bad password), or your browser doesn\'t understand how to supply '
        'the credentials required.'
    )


class Forbidden(HTTPException):

    """*403* `Forbidden`

    Raise if the user doesn't have the permission for the requested resource
    but was authenticated.
    """
    code = 403
    description = (
        'You don\'t have the permission to access the requested resource. '
        'It is either read-protected or not readable by the server.'
    )


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.'
    )
    ...
    ...
    ...

自定制异常对象

这里写图片描述

控制器app/v1/client.py
@api.route('/register', methods=['POST'])
def create_client():
    data = request.json
    form = ClientForm(data=data) # 接收json格式数据指定data=data
    if form.validate():
        promise = {
            ClientTypeEnum.USER_EMAIL: __register_user_by_email,
            ClientTypeEnum.USER_MOBILE: __register_user_by_mobile,
            ClientTypeEnum.USER_MINA: __register_user_by_mobile,
        }
        promise[form.type.data]()       # form.type.data 在验证器中已经换成了枚举类型
    else:
        from app.libs.error_code import My_notfound
        raise My_notfound()
    return "success"   # 暂时

虽然上面能返回异常,但是返回的是html格式,不是json格式,我们要深度定制HTTPException


2.自定义APIException

app/libs/my_exception.py

# -*- coding: utf-8 -*-
# @Author: Lai
from werkzeug.exceptions import HTTPException
from flask import request, json

class APIException(HTTPException):
    code = 500
    msg = "this is valid"    # 相当于description
    error_code = 999

    # 此处相当巧妙,若找不到实例属性,会直接找类属性
    def __init__(self, code=None, msg=None, error_code=None, response=None):
        super(APIException, self).__init__(msg,response)
        if code:
            self.code = code
        if msg:
            self.msg = msg
        if error_code:
            self.error_code = error_code

    def get_body(self, environ=None):
        request_data = request.method + " " + request.full_path.split('?')[0]
        data_dict = {
            "code": self.code,
            "msg": self.msg,
            "error_code": self.error_code,
            "request": request_data
        }
        return json.dumps(data_dict)

    def get_headers(self, environ=None):
        return [('Content-Type', 'application/json')]

改写上面的app/libs/error_code.py

# -*- coding: utf-8 -*-
# @Author: Lai

from app.libs.my_exception import APIException

class My_notfound(APIException):
    code = 404
    msg = "not found"
    error_code = 1006
控制器app/v1/client.py
@api.route('/register', methods=['POST'])
def create_client():
    data = request.json
    form = ClientForm(data=data) # 接收json格式数据指定data=data
    if form.validate():
        promise = {
            ClientTypeEnum.USER_EMAIL: __register_user_by_email,
            ClientTypeEnum.USER_MOBILE: __register_user_by_mobile,
            ClientTypeEnum.USER_MINA: __register_user_by_mobile,
        }
        promise[form.type.data]()       # form.type.data 在验证器中已经换成了枚举类型
    else:
        from app.libs.error_code import My_notfound
        raise My_notfound()
    return "success"   # 暂时

此时抛出的就是json格式异常了,但是问题又来了,验证器的返回错误多种多样,如何通过抛出一个异常匹配多种错误。

解决办法:
这里写图片描述
这里写图片描述

结果

这里写图片描述

3.重写WTForms

我们想要在验证器中抛异常,但是WTForms是不会抛异常的,我们开始自定制WTForms的Form类

这里写图片描述

在app/validators/forms.py下,必须导入我们自定制的Form,然后在控制器app/v1/client.py下

# -*- coding: utf-8 -*-
# @Author: Lai
from app.libs.red_print import Red_Print
from app.models.user import User
from app.validators.forms import ClientForm, EmailClientForm
from app.libs.enums import ClientTypeEnum
from flask import request

api = Red_Print('client')

@api.route('/register', methods=['POST'])
def create_client():
    data = request.json
    form = ClientForm(data=data).validate_for_api()
    promise = {
        ClientTypeEnum.USER_EMAIL: __register_user_by_email,
        ClientTypeEnum.USER_MOBILE: __register_user_by_mobile,
        ClientTypeEnum.USER_MINA: __register_user_by_mobile,
    }
    promise[form.type.data]()       # form.type.data 在验证器中已经换成了枚举类型
    return "ok"   # 暂时

def __register_user_by_email():
    data = request.json
    form = EmailClientForm(data=data).validate_for_api()
    User.register_by_email(form.nickname.data,
                           form.account.data,
                           form.password.data)

def __register_user_by_mobile():
    pass

def __register_user_by_mina():
    pass

就实现了再验证器中抛出异常。


4.已知异常与未知异常

manage.py中(只有flask1.0以上才能捕获Exception)

# -*- coding: utf-8 -*-
# @Author: Lai

# -*- coding: utf-8 -*-
# @Author: Lai
from app import create_app
from app.models import db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from app.libs.my_exception import APIException
from werkzeug.exceptions import HTTPException
from app.libs.error_code import ServerError # 自定义

app = create_app()
# manager = Manager(app)
# migrate = Migrate(app, db)
# manager.add_command('db', MigrateCommand)

@app.errorhandler(Exception)
def frame_error(e):
    if isinstance(e, APIException):
        return e
    elif isinstance(e, HTTPException):  # 若异常时HTTPException类型按APIException抛出
        msg = e.description
        code = e.code
        error_code = 1007
        return APIException(msg=msg, code=code, error_code=error_code)
    else:
        #log
        if not app.config['DEBUG']:
            return ServerError()    #  未知异常的抛出
        else:
            raise e

if __name__ == "__main__":
    app.run(debug=app.config['DEBUG'])

猜你喜欢

转载自blog.csdn.net/lilied001/article/details/80786991