赞
踩
flask源码解析
初始化时收集
flask init时
if self.has_static_folder:
assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination'
self.add_url_rule(
self.static_url_path + '/<path:filename>',
endpoint='static',
host=static_host,
view_func=self.send_static_file
)
初始化时读到@app.route()就添加
def route(self, rule, **options): """A decorator that is used to register a view function for a given URL rule. This does the same thing as :meth:`add_url_rule` but is intended for decorator usage:: @app.route('/') def index(): return 'Hello World' For more information refer to :ref:`url-route-registrations`. :param rule: the URL rule as string :param endpoint: the endpoint for the registered URL rule. Flask itself assumes the name of the view function as endpoint :param options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object. A change to Werkzeug is handling of method options. methods is a list of methods this rule should be limited to (``GET``, ``POST`` etc.). By default a rule just listens for ``GET`` (and implicitly ``HEAD``). Starting with Flask 0.6, ``OPTIONS`` is implicitly added and handled by the standard request handling. """ def decorator(f): endpoint = options.pop('endpoint', None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
读到@index_bp.route()即添加
self.url_adapter = app.create_url_adapter(self.request)
实际调用 添加Rule对象
return Map.bind(
self,
server_name,
script_name,
subdomain,
environ["wsgi.url_scheme"],
environ["REQUEST_METHOD"],
path_info,
query_args=query_args,
)
无论是哪种定义方式 均是由Flask的add_url_rule()
先定义endpoint 若没有则 获取view_func的.name
再获取所有的methods 未设置则给默认值GET
rule = self.url_rule_class(rule, methods=methods, **options)中的rule是一个Rule对象 rule入参是route中的参数
添加到rule_map中 同时去view_functions中根据endpoint去获取值 以此来去重 如为获取到值 就新增
当请求进入flask时 同样 根据请求url来做endpoint从view_functions取值来分发请求
生成request对象把请求的method args 包装起来供在视图函数中使用 同时先生成app上下文 再生成request上下文 并开启session 并把根据正则 match出 请求对应的view_func
在分发请求前 chain()调用请求钩子 再到视图函数
视图函数业务逻辑处理完成之后 经由flask的finalize_request的make_response 包装成一个可返回的Response对象 基本就是补充默认的 headers 和 status
此处的app是一个DispatchingApp 加上()之后触发了他的__call__方法
https://img-blog.csdnimg.cn/3d845ea96788491e9bf9481c03756538.png
生成request对象
RequestContext的主要方法 push pop
push方法主要是生成一个request app上下文 并从此开启session
RequestContext对象 init时 生成self.request = app.request_class(environ)
app.request_class就是Request
request因此有了json的相关方法 并带有自身的endpoint blueprint的属性
init时还有一个match_request方法
最终返回值 rule.endpoint, rv rv应是request.view_args
并在match_request方法中给request.url_rule赋值 即endpoint
_app_ctx_stack是一个LocalStack对象
LocalStack 包含push pop方法及top属性 因此 app_ctx =self._local.stack[-1]
此时是未取到stack的值 获取到了None的
当app_ctx是None或者 app_ctx.app != self.app时 调用app_ctx.push()
push 实际就是 self._local.stack在此处被赋值
def full_dispatch_request(self): """Dispatches the request and on top of that performs request pre and postprocessing as well as HTTP exception catching and error handling. .. versionadded:: 0.7 """ self.try_trigger_before_first_request_functions() try: request_started.send(self) rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) return self.finalize_request(rv)
处理bp的钩子函数
def preprocess_request(self): """Called before the request is dispatched. Calls :attr:`url_value_preprocessors` registered with the app and the current blueprint (if any). Then calls :attr:`before_request_funcs` registered with the app and the blueprint. If any :meth:`before_request` handler returns a non-None value, the value is handled as if it was the return value from the view, and further request handling is stopped. """ bp = _request_ctx_stack.top.request.blueprint funcs = self.url_value_preprocessors.get(None, ()) if bp is not None and bp in self.url_value_preprocessors: funcs = chain(funcs, self.url_value_preprocessors[bp]) for func in funcs: func(request.endpoint, request.view_args) funcs = self.before_request_funcs.get(None, ()) if bp is not None and bp in self.before_request_funcs: funcs = chain(funcs, self.before_request_funcs[bp]) for func in funcs: rv = func() if rv is not None: return rv
处理请求的钩子函数
实际请求分发到对应视图过程
def finalize_request(self, rv, from_error_handler=False): """Given the return value from a view function this finalizes the request by converting it into a response and invoking the postprocessing functions. This is invoked for both normal request dispatching as well as error handlers. Because this means that it might be called as a result of a failure a special safe mode is available which can be enabled with the `from_error_handler` flag. If enabled, failures in response processing will be logged and otherwise ignored. :internal: """ response = self.make_response(rv) try: response = self.process_response(response) request_finished.send(self, response=response) except Exception: if not from_error_handler: raise self.logger.exception('Request finalizing failed with an ' 'error while handling an error') return response
仅返回str时 flask帮我们生成一个可返回的Response对象 并添加默认status headers
# make sure the body is an instance of the response class if not isinstance(rv, self.response_class): if isinstance(rv, (text_type, bytes, bytearray)): # let the response class set the status and headers instead of # waiting to do it manually, so that the class can handle any # special logic rv = self.response_class(rv, status=status, headers=headers) status = headers = None else: # evaluate a WSGI callable, or coerce a different response # class to the correct type try: rv = self.response_class.force_type(rv, request.environ) except TypeError as e: new_error = TypeError( '{e}\nThe view function did not return a valid' ' response. The return type must be a string, tuple,' ' Response instance, or WSGI callable, but it was a' ' {rv.__class__.__name__}.'.format(e=e, rv=rv) ) reraise(TypeError, new_error, sys.exc_info()[2]) # prefer the status if it was provided if status is not None: if isinstance(status, (text_type, bytes, bytearray)): rv.status = status else: rv.status_code = status # extend existing headers with provided headers if headers: rv.headers.extend(headers) return rv
1.再次调用hook
def process_response(self, response): """Can be overridden in order to modify the response object before it's sent to the WSGI server. By default this will call all the :meth:`after_request` decorated functions. .. versionchanged:: 0.5 As of Flask 0.5 the functions registered for after request execution are called in reverse order of registration. :param response: a :attr:`response_class` object. :return: a new response object or the same, has to be an instance of :attr:`response_class`. """ ctx = _request_ctx_stack.top bp = ctx.request.blueprint funcs = ctx._after_request_functions if bp is not None and bp in self.after_request_funcs: funcs = chain(funcs, reversed(self.after_request_funcs[bp])) if None in self.after_request_funcs: funcs = chain(funcs, reversed(self.after_request_funcs[None])) for handler in funcs: response = handler(response) if not self.session_interface.is_null_session(ctx.session): self.session_interface.save_session(self, ctx.session, response) return response
2.并设置响应的cookie
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) # If the session is modified to be empty, remove the cookie. # If the session is empty, return without setting the cookie. if not session: if session.modified: response.delete_cookie( app.session_cookie_name, domain=domain, path=path ) return # Add a "Vary: Cookie" header if the session was accessed at all. if session.accessed: response.vary.add('Cookie') if not self.should_set_cookie(app, session): return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) samesite = self.get_cookie_samesite(app) expires = self.get_expiration_time(app, session) val = self.get_signing_serializer(app).dumps(dict(session)) response.set_cookie( app.session_cookie_name, val, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure, samesite=samesite )
full_dispatch_request调用完成 如果出现异常就是捕获异常
再调用BaseResponse 包装成get_wsgi_response返回
调用ctx.auto_pop(error)时 do_teardown_request被调用
def do_teardown_request(self, exc=_sentinel): """Called after the request is dispatched and the response is returned, right before the request context is popped. This calls all functions decorated with :meth:`teardown_request`, and :meth:`Blueprint.teardown_request` if a blueprint handled the request. Finally, the :data:`request_tearing_down` signal is sent. This is called by :meth:`RequestContext.pop() <flask.ctx.RequestContext.pop>`, which may be delayed during testing to maintain access to resources. :param exc: An unhandled exception raised while dispatching the request. Detected from the current exception information if not passed. Passed to each teardown function. .. versionchanged:: 0.9 Added the ``exc`` argument. """ if exc is _sentinel: exc = sys.exc_info()[1] funcs = reversed(self.teardown_request_funcs.get(None, ())) bp = _request_ctx_stack.top.request.blueprint if bp is not None and bp in self.teardown_request_funcs: funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) for func in funcs: func(exc) request_tearing_down.send(self, exc=exc)
再调用LocalStack的pop 将request上下文清除 再清除app上下文
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self._local, "stack", None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
实际调用了RequestContext.pop(exc)
finally:
rv = _request_ctx_stack.pop()
# get rid of circular dependencies at the end of the request
# so that we don't require the GC to be active.
if clear_request:
rv.request.environ['werkzeug.request'] = None
# Get rid of the app as well if necessary.
if app_ctx is not None:
app_ctx.pop(exc)
该方法又会调用app_ctx.pop(exc) 通过对引用计数 -= 1来回收app_cxt的回收
def pop(self, exc=_sentinel):
"""Pops the app context."""
try:
self._refcnt -= 1
if self._refcnt <= 0:
if exc is _sentinel:
exc = sys.exc_info()[1]
self.app.do_teardown_appcontext(exc)
finally:
rv = _app_ctx_stack.pop()
assert rv is self, 'Popped wrong app context. (%r instead of %r)' \
% (rv, self)
appcontext_popped.send(self.app)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。