新手上路,小心开车
一、add_url_rule
add_url_rule是Flask框架中注册路由的函数(class App(Scaffold)类方法),源代码注释如下:
@setupmethod
def add_url_rule(
self,
rule: str,
endpoint: str | None = None,
view_func: ft.RouteCallable | None = None,
provide_automatic_options: bool | None = None,
**options: t.Any,
) -> None:
"""
endpoint代表视图函数,如果用户没有指定endpoint,则默认是视图函数的__name__属性,即函数
名
"""
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func) # type: ignore
options["endpoint"] = endpoint
"""
methods表示请求方式列表,如果视图函数没有显示约束请求方式,则默认是GET方式;
如果包含GET请求方法,但是没有HEAD方式,也会把HEAD方式添加上(Rule类中实现)
"""
methods = options.pop("methods", None)
# if the methods are not given and the view_func object knows its
# methods we can use that instead. If neither exists, we go with
# a tuple of only ``GET`` as default.
if methods is None:
methods = getattr(view_func, "methods", None) or ("GET",)
if isinstance(methods, str):
raise TypeError(
"Allowed methods must be a list of strings, for"
' example: @app.route(..., methods=["POST"])'
)
methods = {item.upper() for item in methods}
# Methods that should always be added
required_methods: set[str] = set(getattr(view_func, "required_methods", ()))
# starting with Flask 0.8 the view_func object can disable and
# force-enable the automatic options handling.
"""
PROVIDE_AUTOMATIC_OPTIONS:表示是否自动添加OPTIONS请求方式,
要添加OPTIONS请求,要么显示指定OPTIONS请求方法,要么在config字典配置
PROVIDE_AUTOMATIC_OPTIONS=True
"""
if provide_automatic_options is None:
provide_automatic_options = getattr(
view_func, "provide_automatic_options", None
)
if provide_automatic_options is None:
if "OPTIONS" not in methods and self.config["PROVIDE_AUTOMATIC_OPTIONS"]:
provide_automatic_options = True
required_methods.add("OPTIONS")
else:
provide_automatic_options = False
# Add the required methods now.
methods |= required_methods
"""
把当前url转化为Rule对象实例,它包含了当前url的路由、请求方式、视图函数等属性
"""
rule_obj = self.url_rule_class(rule, methods=methods, **options)
rule_obj.provide_automatic_options = provide_automatic_options
"""
所有的Rule对象实例都记录在self.url_map对象中,这是一个Map对象实例
"""
self.url_map.add(rule_obj)
"""
所有的视图函数都记录在self.view_functions字典中,因此endpoint不允许重名
"""
if view_func is not None:
old_func = self.view_functions.get(endpoint)
if old_func is not None and old_func != view_func:
raise AssertionError(
"View function mapping is overwriting an existing"
f" endpoint function: {endpoint}"
)
self.view_functions[endpoint] = view_func
二、wsgi_app
wsgi_app是Flask作为WSGI应用程序的入口(class Flask(App)类方法),源代码注解如下:
def wsgi_app(
self, environ: WSGIEnvironment, start_response: StartResponse
) -> cabc.Iterable[bytes]:
"""The actual WSGI application. This is not implemented in
:meth:`__call__` so that middlewares can be applied without
losing a reference to the app object. Instead of doing this::
app = MyMiddleware(app)
It's a better idea to do this instead::
自定义中间件,不常用
app.wsgi_app = MyMiddleware(app.wsgi_app)
Then you still have the original application object around and
can continue to call methods on it.
"""
"""
生成RequestContext实例,将environ字典转化为request对象, ctx.request
"""
ctx = self.request_context(environ)
error: BaseException | None = None
try:
try:
"""
ctx.push()主要完成三件事:
1.生成AppContext实例,使current_app和g对象可用;
2.加载会话数据,调用session_interface.open_session(),使session对象可以
3.路由匹配,将请求的路由和flask所有注册路由进行匹配,匹配不成功,则把存储错误
信息存储到request.routing_exception属性中,匹配成功则生成Rule对象(包含
endpoint属性)和路由参数字典,并赋值给request.url_rule和
request.view_args属性。
"""
ctx.push()
"""
full_dispatch_request()代码逻辑如下:
1.发送request_started信号;
2.先调用所有url_value_preprocessor() 装饰的函数,然后再调用所有
before_request() 装饰的函数;
3.如果before_request()装饰的函数有任何返回值,则会被当做响应返回,如果没有,则
会调用dispatch_request()方法,找到相应的视图函数执行,并把执行结果返回。
4.如果上述步骤发生异常,则会调用errorhandler() 装饰器函数处理异常;
5.最后调用finalize_request()方法,先把第三步返回的结果通过make_response封装成
response对象,然后任何 after_this_request() 装饰的函数都会被调用,再然后任
何 after_request() 装饰的函数都会被调用,最后使用
session_interface.save_session来持久化任何已修改的会话数据,并发送
request_finished 信号。
"""
response = self.full_dispatch_request()
except Exception as e:
error = e
"""
如果到目前为止的任何步骤引发了一个异常,并且没有被错误处理函数处理,那么现在会被
转换为一个通用的 500 响应,并且got_request_exception 信号被发送。
"""
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
"""
响应对象的状态、头部信息和正文被返回给WSGI服务器。
"""
return response(environ, start_response)
finally:
if "werkzeug.debug.preserve_context" in environ:
environ["werkzeug.debug.preserve_context"](_cv_app.get())
environ["werkzeug.debug.preserve_context"](_cv_request.get())
if error is not None and self.should_ignore_error(error):
error = None
"""
pop()方法主要是清理请求上下文和应用上下文
1.任何 teardown_request() 装饰的函数都被调用,request_tearing_down
信号被发送,请求上下文被弹出, request 和 session 不再可用;
2.任何 teardown_appcontext() 的装饰函数都被调用,appcontext_popped信号被发送,应
用上下文被弹出, current_app 和 g 不再可用。
"""
ctx.pop(error)