Flask 請求源碼分析
阿新 • • 發佈:2018-01-09
which sim hid failure {} 調用 賦值 cau httponly
執行app.run()方法:
def run(self, host=None, port=None, debug=None, **options): from werkzeug.serving import run_simple if host is None: host = ‘127.0.0.1‘ if port is None: server_name = self.config[‘SERVER_NAME‘] if server_name and ‘:‘ in server_name: portrun方法= int(server_name.rsplit(‘:‘, 1)[1]) else: port = 5000 if debug is not None: self.debug = bool(debug) options.setdefault(‘use_reloader‘, self.debug) options.setdefault(‘use_debugger‘, self.debug) try: #執行,self=app---->執行Flask類的__call__方法run_simple(host, port, self, **options) finally: self._got_first_request = False
執行Flask.__call__方法:
#包含請求相關的所有信息 def __call__(self, environ, start_response): """Shortcut for :attr:`wsgi_app`.""" return self.wsgi_app(environ, start_response)
執行wsgi_app()方法:
def wsgi_app(self, environ, start_response): #獲取請求數據,並進行封裝和加工, ctx = self.request_context(environ) #將RequestContext(request,session)封裝在Local中 ‘‘‘ {‘唯一標識‘:{‘stack‘:[RequestContext(request,session),]} } ‘‘‘ ctx.push() error = None try: try: #調用視圖函數 response = self.full_dispatch_request() except Exception as e: error = e response = self.handle_exception(e) except: error = sys.exc_info()[1] raise return response(environ, start_response) finally: if self.should_ignore_error(error): error = None ctx.auto_pop(error)
執行RequestContext.__init__()和push()方法:
__init__():獲取request
def __init__(self, app, environ, request=None): self.app = app if request is None: #實際是一個Request對象,將request信息封裝到Request(environ)中,並賦值給RequestContext對象中
request = app.request_class(environ) self.request = request self.url_adapter = app.create_url_adapter(self.request) self.flashes = None self.session = None
push()方法:將RequestContext對象添加到Local中,獲取/創建session
def push(self): top = _request_ctx_stack.top if top is not None and top.preserved: top.pop(top._preserved_exc) app_ctx = _app_ctx_stack.top if app_ctx is None or app_ctx.app != self.app: app_ctx = self.app.app_context() app_ctx.push() self._implicit_app_ctx_stack.append(app_ctx) else: self._implicit_app_ctx_stack.append(None) if hasattr(sys, ‘exc_clear‘): sys.exc_clear() #將當前requestContext對象放到Local中, _request_ctx_stack.push(self) # Open the session at the moment that the request context is # available. This allows a custom open_session method to use the # request context (e.g. code that access database information # stored on `g` instead of the appcontext). #賦值操作將self.session=SecureCookieSession()
#SecureCookieSession()實際上是一個字典
self.session = self.app.open_session(self.request) if self.session is None:
#用戶第一次進來session={}執行,返回NullSession self.session = self.app.make_null_session()
當請求進來時:執行open_session()方法:
class SecureCookieSessionInterface(SessionInterface): def open_session(self, app, request): s = self.get_signing_serializer(app) if s is None: return None #去cookie中獲取session作為key,所對應的值(包含了當前用戶所有的session數據) val = request.cookies.get(app.session_cookie_name) #沒有 if not val: #返回SecureCookieSession return self.session_class() max_age = total_seconds(app.permanent_session_lifetime) try: #val存在的話 #解密 將加密的字符串解密程字典 data = s.loads(val, max_age=max_age) return self.session_class(data) except BadSignature: return self.session_class()
這是我們已經將RequestContext(request,session)的對象放到Local中,並且request和session的初始值也獲取到了,
上面方法就是為了執行下面兩句:
ctx = self.request_context(environ)
ctx.push()
執行上面的語句後我們在wagi_app()方法中繼續向下執行,
觸發視圖函數
Flask類中
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 """ #執行@before_first_request所裝飾的所有函數 self.try_trigger_before_first_request_functions() try: request_started.send(self) #執行@before_request裝飾的所有函數 看是否有返回值 rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() except Exception as e: rv = self.handle_user_exception(e) #執行@after_request所裝飾的所有函數 return self.finalize_request(rv)
執行finalize_request()方法:
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: #執行@after_request所裝飾的所有函數 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
執行process_response()方法
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) #保存session if not self.session_interface.is_null_session(ctx.session): self.save_session(ctx.session, response) return response
具體是怎麽保存session的呢?
看save_session()方法
session.py中
def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) # Delete case. If there is no session we bail early. # If the session was modified to be empty we remove the # whole cookie. if not session: if session.modified: response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return # Modification case. There are upsides and downsides to # emitting a set-cookie header each request. The behavior # is controlled by the :meth:`should_set_cookie` method # which performs a quick check to figure out if the cookie # should be set or not. This is controlled by the # SESSION_REFRESH_EACH_REQUEST config flag as well as # the permanent flag on the session itself. if not self.should_set_cookie(app, session): return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session)
#加密操作 val = self.get_signing_serializer(app).dumps(dict(session))
#將session保存在cookie中 response.set_cookie(app.session_cookie_name, val, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
globals.py
def _lookup_req_object(name): # top實際是一個RequestContext對象 ‘‘‘ { ‘唯一標識‘:{‘stack‘:[RequestContext(request,session),]} } ‘‘‘ top = _request_ctx_stack.top if top is None: raise RuntimeError(_request_ctx_err_msg) #得到session:RequestContext(request,session).session #得到request:RequestContext(request,session).request return getattr(top, name) def _lookup_app_object(name): top = _app_ctx_stack.top if top is None: raise RuntimeError(_app_ctx_err_msg) return getattr(top, name) def _find_app(): top = _app_ctx_stack.top if top is None: raise RuntimeError(_app_ctx_err_msg) return top.app # context locals _request_ctx_stack = LocalStack() _app_ctx_stack = LocalStack() current_app = LocalProxy(_find_app) request = LocalProxy(partial(_lookup_req_object, ‘request‘)) session = LocalProxy(partial(_lookup_req_object, ‘session‘)) g = LocalProxy(partial(_lookup_app_object, ‘g‘))
Flask 請求源碼分析