from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Saltriver'
@app.errorhandler(404) # <---
def page_not_found(error):
return '---Not Found---', 404
if __name__ == '__main__':
app.run()
@setupmethod
def errorhandler(self, code_or_exception):
# code_or_exception为404或者其他.
def decorator(f):
# 注册.
self._register_error_handler(None, code_or_exception, f)
return f
return decorator
@setupmethod
def _register_error_handler(self, key, code_or_exception, f):
"""
:type key: None|str
:type code_or_exception: int|T<=Exception
:type f: callable
"""
if isinstance(code_or_exception, HTTPException): # old broken behavior
raise ValueError(
'Tried to register a handler for an exception instance {0!r}. '
'Handlers can only be registered for exception classes or HTTP error codes.'
.format(code_or_exception))
exc_class, code = self._get_exc_class_and_code(code_or_exception)
handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {})
handlers[exc_class] = f
# app.py
def _get_exc_class_and_code(exc_class_or_code):
if isinstance(exc_class_or_code, integer_types):
# 如果exc_class_or_code是integer_types,比如示例中传入的参数404
exc_class = default_exceptions[exc_class_or_code]
else:
# 如果exc_class_or_code是,比如, werkzeug.exceptions.BadRequest
exc_class = exc_class_or_code
# 其他类型参数就不得行
assert issubclass(exc_class, Exception)
if issubclass(exc_class, HTTPException):
# 如果是继承自HTTPException的类,那么返回示例和响应的code.
return exc_class, exc_class.code
else:
# 否则code就设置为None,自定义???
return exc_class, None
执行完毕之后, error_handler_spec就有了注册的相应的错误处理程序了.对于示例程序而言,注册完后, Flask.error_handler_spec就变成如下了:
None {
400: {:
},
404: {:
},
None: {:
}
}
Part2
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
error = None
try:
# ------------------------------------------------
try:
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.make_response(self.handle_exception(e))
# ------------------------------------------------
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
下面我们重点看full_dispatch_request(...)
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self) # signal.
rv = self.preprocess_request() # <---
if rv is None:
rv = self.dispatch_request() # <---
except Exception as e:
rv = self.handle_user_exception(e)
response = self.make_response(rv)
response = self.process_response(response)
request_finished.send(self, response=response)
return response
在full_dispatch_request()中,我们又重点看dispatch_request().
def dispatch_request(self):
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
self.raise_routing_exception(req)
...
# ctx.py
def match_request(self):
try:
url_rule, self.request.view_args = \
# 如果无法匹配时,就抛出HTTPException.
self.url_adapter.match(return_rule=True)
self.request.url_rule = url_rule
# 捕获并将self.request.routing_exception设置为e.
except HTTPException as e:
self.request.routing_exception = e
def raise_routing_exception(self, request):
...
if not self.debug \
or not isinstance(request.routing_exception, RequestRedirect) \
or request.method in ('GET', 'HEAD', 'OPTIONS'):
# 抛出异常.
raise request.routing_exception # <---
...