一、flask内置异常处理
要想在flask中处理好异常,有一套自己的异常处理机制,首先,我们必须先知道flask自己是如何处理异常的。去flask的源码里找一找会发现,在flask源码的app.py文件下,有很多会抛出异常的方法,其中拿一个举例:
def handle_exception(self, e):
"""default exception handling that kicks in when an exception
occurs that is not caught. in debug mode the exception will
be re-raised immediately, otherwise it is logged and the handler
for a 500 internal server error is used. if no such handler
exists, a default 500 internal server error message is displayed.
.. versionadded:: 0.3
"""
exc_type, exc_value, tb = sys.exc_info()
got_request_exception.send(self, exception=e)
handler = self._find_error_handler(internalservererror())
if self.propagate_exceptions:
# if we want to repropagate the exception, we can attempt to
# raise it with the whole traceback in case we can do that
# (the function was actually called from the except part)
# otherwise, we just raise the error again
if exc_value is e:
reraise(exc_type, exc_value, tb)
else:
raise e
self.log_exception((exc_type, exc_value, tb))
if handler is none:
return internalservererror()
return self.finalize_request(handler(e), from_error_handler=true)
我们发现在flask内部对于500异常,会抛出这样一个错误类internalservererror()
class internalservererror(httpexception):
......
至此我们发现flask内部异常通过继承这个httpexception类来处理,那么这个httpexception类就是我们研究的重点。
二、httpexception类分析
@implements_to_string
class httpexception(exception):
"""baseclass for all http exceptions. this exception can be called as wsgi
application to render a default error page or you can catch the subclasses
of it independently and render nicer error messages.
"""
code = none
description = none
def __init__(self, description=none, response=none):
super(httpexception, self).__init__()
if description is not none:
self.description = description
self.response = response
@classmethod
def wrap(cls, exception, name=none):
"""create an exception that is a subclass of the calling http
exception and the ``exception`` argument.
the first argument to the class will be passed to the
wrapped ``exception``, the rest to the http exception. if
``e.args`` is not empty and ``e.show_exception`` is ``true``,
the wrapped exception message is added to the http error
description.
.. versionchanged:: 0.15.5
the ``show_exception`` attribute controls whether the
description includes the wrapped exception message.
.. versionchanged:: 0.15.0
the description includes the wrapped exception message.
"""
class newcls(cls, exception):
_description = cls.description
show_exception = false
def __init__(self, arg=none, *args, **kwargs):
super(cls, self).__init__(*args, **kwargs)
if arg is none:
exception.__init__(self)
else:
exception.__init__(self, arg)
@property
def description(self):
if self.show_exception:
return "{}\n{}: {}".format(
self._description, exception.__name__, exception.__str__(self)
)
return self._description
@description.setter
def description(self, value):
self._description = value
newcls.__module__ = sys._getframe(1).f_globals.get("__name__")
name = name or cls.__name__ + exception.__name__
newcls.__name__ = newcls.__qualname__ = name
return newcls
@property
def name(self):
"""the status name."""
from .http import http_status_codes
return http_status_codes.get(self.code, "unknown error")
def get_description(self, environ=none):
"""get the description."""
return u"<p>%s</p>" % escape(self.description).replace("\n", "<br>")
def get_body(self, environ=none):
"""get the html body."""
return text_type(
(
u'<!doctype html public "-//w3c//dtd html 3.2 final//en">\n'
u"<title>%(code)s %(name)s</title>\n"
u"<h1>%(name)s</h1>\n"
u"%(description)s\n"
)
% {
"code": self.code,
"name": escape(self.name),
"description": self.get_description(environ),
}
)
def get_headers(self, environ=none):
"""get a list of headers."""
return [("content-type", "text/html; charset=utf-8")]
def get_response(self, environ=none):
"""get a response object. if one was passed to the exception
it's returned directly.
:param environ: the optional environ for the request. this
can be used to modify the response depending
on how the request looked like.
:return: a :class:`response` object or a subclass thereof.
"""
from .wrappers.response import response
if self.response is not none:
return self.response
if environ is not none:
environ = _get_environ(environ)
headers = self.get_headers(environ)
return response(self.get_body(environ), self.code, headers)
- 截取这个类比较重要的几个方法分析,
get_headers方法定义了这个返回的响应头,返回的是html文档。 get_body方法定义了返回的响应体,对应也是一段html的内容。- 最后在response中将响应体,状态码,响应头定义好返回。
分析至此,其实这个httpexception中做的事也不难理解,就是定义好响应体,状态码,还有响应头,做了一个返回。当然这个类返回是html类型的,现在前后端分离交互都是json形式的返回,所以我们可以继承自这个类,定义我们自己的异常处理类。
三、自定义异常处理类
首先我们理解我们自己的这个异常处理类,应该继承自httpexception来改写。而我们自定义的内容应该包含以下几点:
- 需要定义我们自己想要返回的错误信息的json格式,比如内部错误码、错误信息等我们想记录的信息。
- 需要更改返回的响应头,返回json格式的信息响应头就应该设为
'content-type': 'application/json' - 同样需要和httpexception一样定义好状态码
如下定义我们自己的异常类apiexception,返回的信息包括内部错误码,错误信息,请求的url
class apiexception(httpexception):
code = 500
msg = 'sorry, we made a mistake!'
error_code = 999
def __init__(self, msg=none, code=none, error_code=none, headers=none):
if code:
self.code = code
if error_code:
self.error_code = error_code
if msg:
self.msg = msg
super(apiexception, self).__init__(msg, none)
def get_body(self, environ=none):
body = dict(
msg=self.msg,
error_code=self.error_code,
request=request.method + ' ' + self.get_url_no_param()
)
text = json.dumps(body)
return text
def get_headers(self, environ=none):
"""get a list of headers."""
return [('content-type', 'application/json')]
@staticmethod
def get_url_no_param():
full_path = str(request.full_path)
main_path = full_path.split('?')
return main_path[0]
四、方便的定义自己的错误类
有了上面我们改写好的apiexception类,我们就可以自由的定义各种状态码的错误以及对应的错误信息,然后在合适的位置抛出。比如:
class success(apiexception):
code = 201
msg = 'ok'
error_code = 0
class deletesuccess(apiexception):
code = 202
msg = 'delete ok'
error_code = 1
class updatesuccess(apiexception):
code = 200
msg = 'update ok'
error_code = 2
class servererror(apiexception):
code = 500
msg = 'sorry, we made a mistake!'
error_code = 999
class parameterexception(apiexception):
code = 400
msg = 'invalid parameter'
error_code = 1000
class notfound(apiexception):
code = 404
msg = 'the resource are not found'
error_code = 1001
class authfailed(apiexception):
code = 401
msg = 'authorization failed'
error_code = 1005
class forbidden(apiexception):
code = 403
error_code = 1004
msg = 'forbidden, not in scope'
有了这些自定义的错误类,我们不仅可以直接在需要的地方抛出,而且有了自定义的错误码,发生错误时,只要对照错误码去查找对应的错误类,非常方便。而且特别说明的是,虽然说是错误类,但是也是可以定义响应成功的返回的,比如上面定义的200,201的类,同样可以作为一个成功的返回。
使用演示:
user = user.query.first()
if not user:
raise notfound()
五、注意事项
尽管我们可以在我们认为可能出错的所有地方,继承自己的异常类,定义自己的错误类,然后抛出,但是也不是所有的异常都是我们可以提前预知的。比如我们接受前端传来的参数,参数类型或取值范围不正确,这些我们可以预知并处理好,但是如果是逻辑处理中出现了问题,这些不是我们程序员可以控制并处理。所以光有自定义错误类还不够,我们还需要在全局捕获异常来判断,利用aop思想。
# 全局错误aop处理
@app.errorhandler(exception)
def framework_error(e):
api_logger.error("error info: %s" % e) # 对错误进行日志记录
if isinstance(e, apiexception):
return e
if isinstance(e, httpexception):
code = e.code
msg = e.description
error_code = 1007
return apiexception(msg, code, error_code)
else:
if not app.config['debug']:
return servererror()
else:
return e
这里对于flask中抛出的所有的错误进行捕获,然后先进行日志的记录。然后判断如果是我们自定义的apiexception,就直接返回。如果不是我们自定义的,但是是flask处理的httpexception,包装成我们自定义的apiexception再返回。如果都不是的话,说明是服务器出现的其他错误,问题一般出在我们的代码上,在生产环境下,一般统一返回一个500错误,在调试模式下,可以原样返回,便于我们定位修改自己的代码。
以上就是详解flask开发技巧之异常处理的详细内容,更多关于flask异常处理的资料请关注其它相关文章!
你不爱我说白了你就是贱