I usually use in the development, or learn some of the flask development skills, need to have a more solid basis for flask.
1. Flash built in exception handling
In order to handle exceptions well in flask, there is a set of exception handling mechanism. First of all, we must know how flask handles exceptions. Go to the source code of flask and find out that it's in the source code of flask app.py Under the file, there are many methods that throw exceptions. For example:
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)
We found that for the 500 exception within the flash, such an error class InternalServerError() will be thrown
class InternalServerError(HTTPException): ......
So far, we find that the internal exception of flag is handled by inheriting the HTTPException class, which is the focus of our research.
2. HTTPException class analysis
@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) ......
-
Analysis of several important methods for intercepting this class, get_ The headers method defines the response header returned, which is an html document.
-
get_ The body method defines the returned response body, which is also the content of a piece of html.
-
Finally, define the Response body, status code and Response header to return.
So far, it's not hard to understand what HTTPException does. It's to define the response body, status code, and response header, and make a return. Of course, the return of this class is of html type. Now, the front and back-end separation interaction is in the form of json, so we can inherit from this class and define our own exception handling class.
3. Custom exception handling class
First of all, we understand that our own exception handling class should be overridden by inheriting from HTTPException. Our customized content should include the following points:
- We need to define the json format of the error information we want to return, such as the internal error code, error information and other information we want to record.
- The returned response header needs to be changed. The information response header returned in json format should be set to 'content type': 'application / json'
- You need to define the status code as well as HTTPException
Define our own exception class APIException as follows. The returned information includes internal error code, error information and requested 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]
4. Easily define your own error class
With the APIException class we have rewritten above, we are free to define the errors of various status codes and corresponding error information, and then throw them at the appropriate location. For example:
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'
With these custom error classes, we can not only throw them directly where we need them, but also have custom error codes. When an error occurs, it is very convenient to look up the corresponding error classes by referring to the error codes. In particular, although it is an error class, it can also be used to define the successful return of the response. For example, the 200201 class defined above can also be used as a successful return.
Use Demo:
user = User.query.first() if not user: raise NotFound()
5. Precautions
Although we can inherit our own exception class, define our own error class and throw it everywhere we think it may be wrong, not all exceptions can be predicted in advance. For example, if we accept the parameters from the front end, the parameter type or value range is not correct, we can predict and handle them well, but if there is a problem in the logic processing, these are not under the control of our programmers. So it's not enough to just have custom error classes. We also need to catch exceptions in the global to judge, using the AOP idea.
# Global error AOP processing @app.errorhandler(Exception) def framework_error(e): api_logger.error("error info: %s" % e) # Log errors 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
Here, all the errors thrown in the flash are captured, and then the log is recorded first. Then judge if it is our customized APIException, and return it directly. If it's not our custom HTTPException, but it's handled by flash, it's packaged as our custom APIException and returned. If not, it means that there are other errors on the server. Generally, the problem lies in our code. In the production environment, a 500 error is returned uniformly. In the debugging mode, it can be returned as is, so that we can locate and modify our code.
About the exception handling of flask, the above are some skills I have mastered at present. If there are any mistakes, please point out.
Blog Park: https://www.cnblogs.com/luyuze95/
GitHub: https://github.com/luyuze95