目录:
- 涉及知识点
- Flask框架原理
- 简单示例
- 路由系统原理源码分析
- 请求流程简单源码分析
- 响应流程简单源码分析
- session简单源码分析
涉及知识点
1、装饰器
闭包思想
def wapper(func):
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
"""
1. 立即执行wapper函数,并将下面装饰的函数当做参数传递
2. 将wapper函数返回值获取,在index赋值
index = inner函数
"""
@wapper
def index():
print('函数内容')
# 实际执行的 inner函数,inner函数内部调用原函数
index()
View Code
ps.@functools.wraps,以上我们知道了python实现闭包,实际是index = inner(index)的封装思想。但不可避免的是inner封装后,会对封装的函数隐藏一些信息。如:包装异常,隐藏异常,打印日志,统计函数使用时间等。@functools.wraps通过update_wrapper函数,用参数wrapped表示的函数对象(例如:square)的一些属性(如:__name__、 __doc__)覆盖参数wrapper表示的函数对象(例如:callf,这里callf只是简单地调用square函数,因此可以说callf是 square的一个wrapper function)的这些相应属性。
import functools
def wapper(func):
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@wapper
def index():
print('函数内容')
@wapper
def order():
print('函数内容')
print(index.__name__)
print(order.__name__)
View Code
2、面向对象封装
class Foo(object):
def __init__(self,age,name):
self.age = age
self.name = name
class Bar(object):
def __init__(self,counter):
self.counter = counter
self.obj = Foo('18','石鹏')
b1 = Bar(1)
print(b1.obj.name)
View Code
3、python对象什么后面可以加括号
- 函数
- 类
- 方法
- 对象
def f1():
print('f1')
class F2(object):
pass
class F3(object):
def __init__(self):
pass
def ff3(self):
print('ff3')
class F4(object):
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('f4')
def func(arg):
"""
由于arg在函数中加括号,所以他只有4中表现形式:
- 函数
- 类
- 方法
- 对象
:param arg:
:return:
"""
arg()
# 1. 函数,内部执行函数
func(f1)
# 2. 类,内部执行__init__方法
func(F2)
# 3. 方法,obj.ff3
obj1 = F3()
func(obj1.ff3)
# 4. 对象
obj2 = F4()
func(obj2)
View Code
4、call方法
class F4(object):
def __init__(self):
print('构造方法')
def __call__(self, *args, **kwargs):
print('f4')
def run(self,str1):
print("run:%s" % str1)
obj = F4()
obj()
obj.run('sssss')
View Code
5、函数和方法的区别
在于调用时有没有实例化对象,即跟某个对象关联。
from types import MethodType,FunctionType
class F3(object):
def __init__(self):
pass
def ff3(self):
print('ff3')
#
v1 = isinstance(F3.ff3,MethodType) # 方法
v2 = isinstance(F3.ff3,FunctionType) # 函数
print(v1,v2) # False,True
obj = F3()
v1 = isinstance(obj.ff3,MethodType) # 方法
v2 = isinstance(obj.ff3,FunctionType) # 函数
print(v1,v2) # True False
View Code
Flask框架原理
1、框架本质为通过socket模块实现工作流的请求和响应。
通过socket建立实例,accept等待请求地址,并通过编写路由系统来给予相应的响应。
import socket
def main():
# 创建老师
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8000))
sock.listen(5)
while True:
# 老师等待 用户请求的到来
connection, address = sock.accept()
# 获取发送的内容:吴亦凡有没有女朋友?
buf = connection.recv(1024)
# 根据请求URL的不同:
# 回答:没有
connection.send(b"HTTP/1.1 200 OK\r\n\r\n")
connection.send(b"No No No")
# 关闭连接
connection.close()
if __name__ == '__main__':
main()
2、flask通过werkzeug模块来帮助我们完成socket性能。
"""
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
# 当请求打来之后,自动执行:hello()
run_simple('localhost', 4000, hello)
"""
from werkzeug.wrappers import Request, Response
from werkzeug.serving import run_simple
class Foo(object):
def __call__(self, *args, **kwargs):
return Response('Hello World!')
if __name__ == '__main__':
# 当请求打来之后,自动执行:hello()
obj = Foo()
run_simple('localhost', 4000, obj)
View Code
3、flask快速入门
"""
pip install flask
pip3 install flask
"""
from flask import Flask
# 1. 实例化Flask对象
app = Flask('xxxx')
"""
1. 执行 app.route('/index')并获取返回值 xx
2.
@xx
def index():
return 'Hello World'
3. 执行 index = xx(index)
本质:
{
'/index': index
}
"""
@app.route('/index')
def index():
return 'Hello World'
if __name__ == '__main__':
app.run()
View Code
简单示例
1、实现简单登陆
import functools
from flask import Flask,render_template,request,redirect,session
app = Flask('xxxx',template_folder="templates")
app.secret_key = 'as923lrjks9d8fwlkxlduf'
def auth(func):
@functools.wraps(func)
def inner(*args,**kwargs):
user_info = session.get('user_info')
if not user_info:
return redirect('/login')
return func(*args,**kwargs)
return inner
"""
{
/order: inner函数, name: order
/index: inner函数, name: index
}
"""
@app.route('/order',methods=['GET'])
@auth
def order():
user_info = session.get('user_info')
if not user_info:
return redirect('/login')
return render_template('index.html')
@app.route('/index',methods=['GET'])
@auth
def index():
return render_template('index.html')
@app.route('/login',methods=['GET','POST'])
def login():
if request.method == "GET":
return render_template('login.html')
else:
user = request.form.get('user')
pwd = request.form.get('pwd')
if user == 'alex' and pwd == '123':
session['user_info'] = user
return redirect('/index')
# return render_template('login.html',msg = "用户名或密码错误",x = 123)
return render_template('login.html',**{'msg':'用户名或密码错误'})
@app.route('/logout',methods=['GET'])
def logout():
del session['user_info']
return redirect('/login')
if __name__ == '__main__':
app.run()
app.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<form method="post">
<input type="text" name="user">
<input type="password" name="pwd">
<input type="submit" value="提交">{{msg}}
</form>
</body>
</html>
templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>欢迎进入系统</h1>
<img src="/static/111.png" alt="">
</body>
</html>
templates/index.html
2、flask配置文件
import functools
from flask import Flask
# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
# 配置:secret_key
app.secret_key = 'as923lrjks9d8fwlkxlduf'
# 导入配置文件
app.config.from_object('settings.TestingConfig')
@app.route('/index')
def index():
return "index"
if __name__ == '__main__':
app.run()
View Code
class BaseConfig(object):
DEBUG = False
SESSION_REFRESH_EACH_REQUEST = True
class ProConfig(BaseConfig):
pass
class DevConfig(BaseConfig):
DEBUG = True
class TestingConfig(BaseConfig):
DEBUG = True
settings.py
ps.import importlib模块,模块支持传递字符串来导入模块。我们先来创建一些简单模块一遍演示。我们在模块里提供了相同接口,通过打印它们自身名字来区分。可通过importlib.import_module(module_path)来动态导入。其等价于import module_path。
路由系统原理源码分析
1、总体流程:
1.初始化Flask类,Rule类,Map类
2.调用app.route方法
3.route方法调用add_url_rule方法
4. add_url_rule方法rule = self.url_rule_class调用Rule方法,封装url和试图函数
5.add_url_rule方法调用url_map.add(Rule)对路由的rules进行添加[Rule('/index', 函数),]
6.map类存到self.url_map中,Rule存在url_rule_class中
import functools
from flask import Flask,views
# 配置:模板/静态文件
app = Flask('xxxx',template_folder="templates")
"""
{
'/index': index函数
}
1. decorator = app.route('/index')
2.
@decorator
def index():
return "index"
3. decorator(index)
"""
"""
Map() = [
Rule(rule=/index/ endpoint=None view_func=函数),
]
"""
@app.route('/index')
def index():
return "index"
"""
Map() = [
Rule(rule=/index endpoint=None view_func=函数),
Rule(rule=/order endpoint=None view_func=order),
]
"""
def order():
return 'Order'
app.add_url_rule('/order', None, order)
class TestView(views.View):
methods = ['GET']
def dispatch_request(self):
return 'test!'
app.add_url_rule('/test', view_func=TestView.as_view(name='test')) # name=endpoint
# app.add_url_rule('/test', view_func=view函数) # name=endpoint
def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
class X1View(views.MethodView):
methods = ['GET','POST']
decorators = [auth, ]
def get(self):
return 'x1.GET'
def post(self):
return 'x1.POST'
app.add_url_rule('/x1', view_func=X1View.as_view(name='x1')) # name=endpoint
if __name__ == '__main__':
app.run()
View Code
2、初始化Flask类,Rule类,Map类
#--------------------------------------
# Flask类
class Flask(_PackageBoundObject):
url_rule_class = Rule
def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates',
instance_path=None, instance_relative_config=False,
root_path=None)
self.url_map = Map()
#--------------------------------------
# Role类
@implements_to_string
class Rule(RuleFactory):
def __init__(self, string, defaults=None, subdomain=None, methods=None,
build_only=False, endpoint=None, strict_slashes=None,
redirect_to=None, alias=False, host=None):
if not string.startswith('/'):
raise ValueError('urls must start with a leading slash')
self.rule = string
self.is_leaf = not string.endswith('/')
self.map = None
self.strict_slashes = strict_slashes
self.subdomain = subdomain
self.host = host
self.defaults = defaults
self.build_only = build_only
self.alias = alias
if methods is None:
self.methods = None
else:
if isinstance(methods, str):
raise TypeError('param `methods` should be `Iterable[str]`, not `str`')
self.methods = set([x.upper() for x in methods])
if 'HEAD' not in self.methods and 'GET' in self.methods:
self.methods.add('HEAD')
self.endpoint = endpoint
self.redirect_to = redirect_to
if defaults:
self.arguments = set(map(str, defaults))
else:
self.arguments = set()
self._trace = self._converters = self._regex = self._weights = None
#-------------------
#map类
class Map(object):
default_converters = ImmutableDict(DEFAULT_CONVERTERS)
def __init__(self, rules=None, default_subdomain='', charset='utf-8',
strict_slashes=True, redirect_defaults=True,
converters=None, sort_parameters=False, sort_key=None,
encoding_errors='replace', host_matching=False):
self._rules = []
self._rules_by_endpoint = {}
self._remap = True
self._remap_lock = Lock()
self.default_subdomain = default_subdomain
self.charset = charset
self.encoding_errors = encoding_errors
self.strict_slashes = strict_slashes
self.redirect_defaults = redirect_defaults
self.host_matching = host_matching
self.converters = self.default_converters.copy()
if converters:
self.converters.update(converters)
self.sort_parameters = sort_parameters
self.sort_key = sort_key
for rulefactory in rules or ():
self.add(rulefactory)
3、调用app.route方法
def route(self, rule, **options):
def decorator(f):
endpoint = options.pop('endpoint', None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
4、route方法调用add_url_rule方法
@setupmethod
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options['endpoint'] = endpoint
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, string_types):
raise TypeError('Allowed methods have to be iterables of strings, '
'for example: @app.route(..., methods=["POST"])')
methods = set(item.upper() for item in methods)
# Methods that should always be added
required_methods = 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 = getattr(view_func,
'provide_automatic_options', None)
if provide_automatic_options is None:
if 'OPTIONS' not in methods:
provide_automatic_options = True
required_methods.add('OPTIONS')
else:
provide_automatic_options = False
# Add the required methods now.
methods |= required_methods
rule = self.url_rule_class(rule, methods=methods, **options)
rule.provide_automatic_options = provide_automatic_options
self.url_map.add(rule)
5、 add_url_rule方法rule = self.url_rule_class调用Rule方法,封装url和试图函数
add_url_rule,代码同4 ;通过url_rule_class = Rule实例化,代码同2
6、add_url_rule方法调用url_map.add(Rule)对路由的rules进行添加[Rule('/index', 函数),]
add_url_rule代码同4,调用url_map.add方法
def add(self, rulefactory):
"""Add a new rule or factory to the map and bind it. Requires that the
rule is not bound to another map.
:param rulefactory: a :class:`Rule` or :class:`RuleFactory`
"""
for rule in rulefactory.get_rules(self):
rule.bind(self)
self._rules.append(rule)
self._rules_by_endpoint.setdefault(rule.endpoint, []).append(rule)
self._remap = True
7.map类存到self.url_map中,Rule存在url_rule_class中。
同代码2.
请求流程简单源码分析
1、综述:
1.已生成路由后,由app.run执行run方法
2.run方法通过werkzeug模块执行run_simple方法
3.werkzeug模块会触发__call__方法
4.__call__方法会触发wsgi_app
5.ctx=request_context对象,触发request_context对象
6.request_context对象__init__进行实例化
--request = app.request_class(environ)
7.flask初始化通过request实例化调用Request对象,通过request实例化调用Request对象
8.Request对象通过werkzeug模块__init__进行实例化
9.存储到ctx中
10.ctx.push()调用RequestContest.push方法
2、__call__方法会触发wsgi_app
def __call__(self, environ, start_response):
"""Shortcut for :attr:`wsgi_app`."""
return self.wsgi_app(environ, start_response)
3、ctx=request_context对象,触发request_context对象
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
4、request_context对象__init__进行实例化
def request_context(self, environ):
return RequestContext(self, environ)
--request = app.request_class(environ)
def __init__(self, app, environ, request=None):
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = app.create_url_adapter(self.request)
self.flashes = None
self.session = None
# Request contexts can be pushed multiple times and interleaved with
# other request contexts. Now only if the last level is popped we
# get rid of them. Additionally if an application context is missing
# one is created implicitly so for each level we add this information
self._implicit_app_ctx_stack = []
# indicator if the context was preserved. Next time another context
# is pushed the preserved context is popped.
self.preserved = False
# remembers the exception for pop if there is one in case the context
# preservation kicks in.
self._preserved_exc = None
# Functions that should be executed after the request on the response
# object. These will be called before the regular "after_request"
# functions.
self._after_request_functions = []
self.match_request()
5、flask初始化通过request实例化调用Request对象,通过request实例化调用Request对象
class Flask(_PackageBoundObject):
request_class = Request
6、Request对象通过werkzeug模块__init__进行实例化
class Request(RequestBase):
#: The internal URL rule that matched the request. This can be
#: useful to inspect which methods are allowed for the URL from
#: a before/after handler (``request.url_rule.methods``) etc.
#:
#: .. versionadded:: 0.6
url_rule = None
#: A dict of view arguments that matched the request. If an exception
#: happened when matching, this will be ``None``.
view_args = None
#: If matching the URL failed, this is the exception that will be
#: raised / was raised as part of the request handling. This is
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
#: something similar.
routing_exception = None
# Switched by the request context until 1.0 to opt in deprecated
# module functionality.
_is_old_module = False
7、.存储到ctx中
同3代码
8、ctx.push()调用RequestContest.push方法
同3代码
flask的sesstion流程
1、综述
1.RequestContext.push,调用app.open_sesstion
2.self.session调用app.open_session
3.通过session_interface变量调用到secureCookieSeeionInerface类的open_session
4.如果没有,则session_class = SecureCookieSession,open_session经过loads加密返回self.session_class(),
5.将加密session返回到self.session
6.执行视图函数
response = self.full_dispatch_request()
----调用try_trigger_before_first_request_functions
(before_first_request)
----调用preprocess_request(before_request)
----调用dispatch_request(执行试图函数)
----调用finalize_request(@fater_request)
7.finalize_request
----response = self.process_response(response)
8.process_response
----执行@fater_request函数
----self.save_session(ctx.session, response)
9.通过self.save_session调用save_session并返回SecureCookieSessionInterface.save_session(self, session, response)
10.save_session保存session,报错return null
2、RequestContext.push,调用app.open_sesstion
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
ctx.push()
error = None
try:
try:
# 4 执行视图函数
response = self.full_dispatch_request()
except Exception as e:
# 异常处理试图报错,包含信号2345报错执行,got_request_exception信号
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
# 9、结束
ctx.auto_pop(error)
通过self.session = self.app.open_session(self.request)调用flask.open_session方法
def open_session(self, request):
return self.session_interface.open_session(self, request)
3、self.session调用app.open_session
class RequestContext(object):
def push(self):
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
4、通过session_interface变量调用到secureCookieSeeionInerface类的open_session
class Flask(_PackageBoundObject):
session_interface = SecureCookieSessionInterface()
5、如果没有,则session_class = SecureCookieSession,open_session经过loads加密返回self.session_class(),
class SecureCookieSessionInterface(SessionInterface):
"""The default session interface that stores sessions in signed cookies
through the :mod:`itsdangerous` module.
"""
#: the salt that should be applied on top of the secret key for the
#: signing of cookie based sessions.
salt = 'cookie-session'
#: the hash function to use for the signature. The default is sha1
digest_method = staticmethod(hashlib.sha1)
#: the name of the itsdangerous supported key derivation. The default
#: is hmac.
key_derivation = 'hmac'
#: A python serializer for the payload. The default is a compact
#: JSON derived serializer with support for some extra Python types
#: such as datetime objects or tuples.
serializer = session_json_serializer
session_class = SecureCookieSession
def open_session(self, app, request):
# sission,key值
s = self.get_signing_serializer(app)
if s is None:
return None
# 如果能从cookie拿到session的话
val = request.cookies.get(app.session_cookie_name)
if not val:
return self.session_class() #如果没有session,则返回一个空字典
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age) # 加密保存
return self.session_class(data)
except BadSignature:
return self.session_class() # 返回session类
6、将加密session返回到self.session
class RequestContext(object):
def push(self):
self.session = self.app.open_session(self.request)
if self.session is None:
self.session = self.app.make_null_session()
7、执行视图函数
response = self.full_dispatch_request()
同代码2
----调用try_trigger_before_first_request_functions
(before_first_request)
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()
----调用preprocess_request(before_request)
----调用dispatch_request(执行试图函数)
----调用finalize_request(@fater_request)
def full_dispatch_request(self):
try:
# 执行特殊装饰器:before_request装饰的所有函数
# 如果没有返回值,rv=None;有返回值 “嘻嘻嘻”
rv = self.preprocess_request()
if rv is None:
# 触发执行视图函数,使用session
rv = self.dispatch_request()
except Exception as e:
rv = self.handle_user_exception(e)
# 6 对返回值进行封装,执行@fater_request装饰器;session保存
return self.finalize_request(rv)
8、finalize_request
----response = self.process_response(response)
def finalize_request(self, rv, from_error_handler=False):
''' 创建返视图返回'''
response = self.make_response(rv)
try:
'''返回值'''
response = self.process_response(response)
# 执行信号request_finished
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
9、process_response
----执行@fater_request函数
----self.save_session(ctx.session, response)
def process_response(self, response):
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]))
# 执行 after_request装饰器
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
10、通过self.save_session调用save_session并返回SecureCookieSessionInterface.save_session(self, session, response)
# Flask类
def save_session(self, session, response):
return self.session_interface.save_session(self, session, response)
11、save_session保存session,报错return null
SecureCookieSessionInterface类:
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)) # 加密
response.set_cookie(app.session_cookie_name, val, # 最后保存在cookie中
expires=expires, httponly=httponly,
domain=domain, path=path, secure=secure)
附录