1.什么是wsgi
wsgi
wsgi是一种规范.全称Python Web Server Gateway Interface,指定了web服务器和Python web应用或web框架之间的标准接口.WSGI规定,Web程序必须有一个可调用对象,且该可调用对象接收两个参数,返回一个可迭代对象:
environ:字典,包含请求的所有信息
start_response:在可调用对象中调用的函数,用来发起响应,参数包括状态码,headers等
uwsgi
uwsgi是uWSGI服务器的独占协议.
uWSGI
uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等.
符合wsgi的application
1 def application(environ,srart_response):
2 start_response('200 OK', [('Content-Type', 'text/plain')])
3 return ['This is a python application!']
1 class ApplicationClass(object):
2 def __init__(self, environ, start_response):
3 self.environ = environ
4 self.start_response = start_response
5
6 def __iter__(self):
7 self.start_response('200 OK', [('Content-type', 'text/plain')])
8 yield "Hello world!n"
2.什么是werkzug
werkzeug
werkzeug是一个WSGI工具包,提供了Request和Response.
flask中的werkzeug
Flask中的程序实例app就是一个可调用对象,我们创建app实例时所调用的Flask类实现了__call方法,__call方法调用了wsgi_app()方法,该方法完成了请求和响应的处理,WSGI服务器通过调用该方法传入请求数据,获取返回数据
1 def wsgi_app(self, environ, start_response): 2 ctx = self.request_context(environ) 3 error = None 4 try: 5 try: 6 ctx.push() 7 response = self.full_dispatch_request() 8 except Exception as e: 9 error = e 10 response = self.handle_exception(e) 11 except: # noqa: B001 12 error = sys.exc_info()[1] 13 raise 14 return response(environ, start_response) 15 finally: 16 if self.should_ignore_error(error): 17 error = None 18 ctx.auto_pop(error) 19 20 def __call__(self, environ, start_response): 21 return self.wsgi_app(environ, start_response)
本质
1 from werkzeug.serving import run_simple
2 from werkzeug.wrappers import BaseResponse
3
4 def func(environ,start_response):
5 print('请求来了')
6 response = BaseResponse('你好')
7 return response(environ,start_response)
8
9 if __name__ == '__main__':
10 run_simple('127.0.0.1',5000,func)
3.flask的快速使用
from flask import Flask, render_template, jsonify, request, redirect, url_for, session
# template_folder模板目录 static_folder 静态目录 static_url_path 改变url的path
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
# app密钥
app.secret_key = 's56h5h45h54s5sfs545agd6'
# 登录认证
def auth(func):
@functools.wraps(func)
def inner(*args, **kwargs):
username = session.get('xxx')
if not username:
# redirect 重定向跳转 url_for 反向生成 等价于redirect('/login')
return redirect(url_for('login'))
return func(*args, **kwargs)
return inner
# 请求之前中间件
@app.before_request
def f_before():
pass
# 请求之后中间件
@app.after_request
def f_after(response):
return response
# 在对应用程序实例的第一个请求之前注册要运行的函数, 只会执行一次
@app.before_first_request
def bfr():
pass
# 主页 endpoint起别名
@app.route('/', methods=['GET'], endpoint='index')
@auth
def index():
DATA_DICT = {}
return render_template('index.html', data_dict=DATA_DICT)
# 登录 endpoint默认等于'/login'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
# return 'hello' # 返回HttpResponse响应
# return jsonify({'code':1000,'data':[1,2,3]}) # 返回json数据
return render_template('login.html')
user = request.form.get('user', '0')
if user == '1':
session['xxx'] = 'yyy'
return redirect('/index')
return render_template('login.html', error='error')
# 路径传参
@app.route('/delete/<nid>', methods=['GET'])
@auth
def delete(nid):
print(nid)
return redirect(url_for('idx'))
# 启动flask
if __name__ == '__main__':
app.run()
4.蓝图及其他
1 from flask import Blueprint, render_template, request, redirect, session, url_for
2
3 loginroute = Blueprint('loginroute', __name__)
4
5 @loginroute.route('/login', methods=['GET', 'POST'], endpoint='login')
6 def login():
7 return render_template('login.html', error=error)
8
9 # 主app中注册蓝图
10 def create_app():
11 app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
12 app.secret_key = 'df2hfdghjdf5gj'
13 app.register_blueprint(login.loginroute, url_prefix='/blog')
14 return app
15 class UserView(views.MethodView):
16 methods = ['GET', 'POST']
17 decorators = [test, ]
18
19 def get(self):
20 return 'get'
21
22 def post(self):
23 return 'POST'
24 app.add_url_rule('/user',view_func=UserView.as_view('user'))
25 # 如果不用方法视图实现需要在普通视图内部调用request.method判断是否为GET,POST进行判断
5.threading.local
1 # 自定义threading_local()
2 # storage = {
3 # 1001:{'x1':1},
4 # 1002:{'x1':2},
5 # 1003:{'x1':3},
6 # 1004:{'x1':4},
7 # }
8 class Local(object):
9 def __init__(self):
10 # storage = {}
11 object.__setattr__(self, 'storage', {})
12
13 def __setattr__(self, key, value):
14 ident = threading.get_ident()
15 if ident in self.storage:
16 self.storage[ident][key] = value
17 else:
18 self.storage[ident] = {key: value}
19
20 def __getattr__(self, item):
21 ident = threading.get_ident()
22 if ident not in self.storage:
23 return
24 return self.storage[ident].get(item)
25 # 自定义高级threading_local()
26 # storage = {
27 # 1001:{'x1':[1,2]},
28 # 1002:{'x1':[]},
29 # 1003:{'x1':[3,]},
30 # 1004:{'x1':[4,5,6]},
31 # }
32 class Local(object):
33 def __init__(self):
34 # storage = {}
35 object.__setattr__(self, 'storage', {})
36
37 def __setattr__(self, key, value):
38 ident = threading.get_ident()
39 if ident in self.storage:
40 self.storage[ident][key].append(value)
41 else:
42 self.storage[ident] = {key: [value,]}
43
44 def __getattr__(self, item):
45 ident = threading.get_ident()
46 if ident not in self.storage:
47 return
48 return self.storage[ident][item][-1]
6.LocalStack和Local对象实现栈
1 """
2 __storage__={
3 1111:{"stack":[xx,yy]}
4 }
5 """
6
7
8 class LocalStack(object):
9 def __iter__(self):
10 self._local = Local()
11
12 def push(self, obj):
13 rv = getattr(self._local, 'stack', None)
14 if rv is None:
15 self._local.stack = rv = []
16 rv.append(obj)
17 return rv
18
19 def pop(self):
20 stack = getattr(self._local, 'stack', None)
21 if stack is None:
22 return None
23 elif len(stack) == 1:
24 return stack[-1]
25 else:
26 return stack.pop()
27
28 def top(self):
29 try:
30 return self._local.stack[-1]
31 except (AttributeError,IndexError):
32 return None
7.Flask全过程
1 app = Flask(__name__,static_url_path='/xx')
2
3 # 在源码种生成如下数据
4 url_rule_class = Rule
5 url_map_class = Map
6 session interface = SecureCookieSessionInterface()
7 # Flask中的__init__
8 self.static_url_path = static_url_path
9 self.config = Config
10 self.view_functions = {
11 "index":index
12 }
13 self.before_request_funcs = {}
14 self.after_request_funcs = {}
15 self.before_first_request = []
16 self.url_map = self.url_map_class()
17 self.add_url_rule("static")
18 # 从字典中读取配置信息
19 app.config.from_object("xx.xx")
20 @app.before_request
21 def f_before():
22 pass
23 # 将f_before添加到self.before_request_funcs
24 self.before_request_funcs['None'].append(f_before)
25 @app.after_request
26 def f_after(response):
27 return response
28 # 将f_after添加到self.after_request_funcs
29 self.after_request_funcs['None'].append(f_after)
30 # self.after_request_funcs中的中间件逆序执行
31 @app.before_first_request
32 def bfr():
33 pass
34 # bfr.before_first_request_funcs
35 self.before_first_request_funcs['None'].append(bfr)
36 @app.route('/', methods=['GET'], endpoint='index')
37 @auth
38 def index():
39 DATA_DICT = {}
40 return render_template('index.html', data_dict=DATA_DICT)
41 #
42 self.add_url_rule
43 # 将url和对应的视图函数名绑定到rule,再将rule和endpoint放到Map,通过as.view方法实现view_func函数的传入
44 # Rule
45 endpoint和url
46 # Map
47 __init__
48 self.rules = [rule,rule]
49 # 启动flask
50 if __name__ == '__main__':
51 app.run()
52 # 请求到来之后,执行__call__方法
53 # run_simple(*,*,self,*)
54 # 执行第三个函数(self)
55 def wsgi_app(self, environ, start_response):
56 """
57 创建ctx,包含request=Request(environ) session=None
58 详见8.1
59 """
60 ctx = self.request_context(environ)
61 error = None
62 try:
63 try:
64 """
65 app_ctx = AppContext(app,g)
66 将app_ctx放入local中
67 将ctx放入local
68 session赋值
69 路由匹配
70 详见8.1
71 """
72 ctx.push()
73 """
74 详见8.4
75 """
76 response = self.full_dispatch_request()
77 except Exception as e:
78 error = e
79 response = self.handle_exception(e)
80 except: # noqa: B001
81 error = sys.exc_info()[1]
82 raise
83 return response(environ, start_response)
84 finally:
85 if self.should_ignore_error(error):
86 error = None
87 ctx.auto_pop(error)
88
89 def __call__(self, environ, start_response):
90 return self.wsgi_app(environ, start_response)
91
92 def full_dispatch_request(self) -> Response:
93 # 触发所有first_request函数
94 self.try_trigger_before_first_request_functions()
95 try:
96 # 信号 暂留
97 request_started.send(self)
98 # 执行所有before_request
99 rv = self.preprocess_request()
100 if rv is None:
101 # 执行视图函数
102 rv = self.dispatch_request()
103 except Exception as e:
104 rv = self.handle_user_exception(e)
105 return self.finalize_request(rv)
106
107 def try_trigger_before_first_request_functions(self) -> None:
108 if self._got_first_request:
109 return
110 with self._before_request_lock:
111 if self._got_first_request:
112 return
113 for func in self.before_first_request_funcs:
114 self.ensure_sync(func)()
115 self._got_first_request = True
116 def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
117 names = (None, *reversed(request.blueprints))
118 for name in names:
119 if name in self.url_value_preprocessors:
120 for url_func in self.url_value_preprocessors[name]:
121 url_func(request.endpoint, request.view_args)
122 for name in names:
123 if name in self.before_request_funcs:
124 for before_func in self.before_request_funcs[name]:
125 rv = self.ensure_sync(before_func)()
126 if rv is not None:
127 return rv
128 return None
129 class RequestContext:
130 def __init__(
131 self,
132 app: "Flask",
133 environ: dict,
134 request: t.Optional["Request"] = None,
135 session: t.Optional["SessionMixin"] = None,
136 ) -> None:
137 self.app = app
138 if request is None:
139 request = app.request_class(environ)
140 self.request = request
141 self.url_adapter = None
142 try:
143 self.url_adapter = app.create_url_adapter(self.request)
144 except HTTPException as e:
145 self.request.routing_exception = e
146 self.flashes = None
147 self.session = session
148 def push(self) -> None:
149 """
150 调用了如下函数,返回了AppContext
151 详见8.2
152 def app_context(self) -> AppContext:
153 return AppContext(self)
154 """
155 app_ctx = self.app.app_context()
156 app_ctx.push()
157 """
158 _request_ctx_stack = LocalStack()
159 _app_ctx_stack = LocalStack()
160 详见8.3
161 """
162 _request_ctx_stack.push(self)
163
164 if self.session is None:
165 session_interface = self.app.session_interface
166 self.session = session_interface.open_session(self.app, self.request)
167
168 if self.session is None:
169 self.session = session_interface.make_null_session(self.app)
170
171 if self.url_adapter is not None:
172 self.match_request()
173 class AppContext:
174 def __init__(self, app: "Flask") -> None:
175 self.app = app
176 self.url_adapter = app.create_url_adapter(None)
177 self.g = app.app_ctx_globals_class()
178
179 # Like request context, app contexts can be pushed multiple times
180 # but there a basic "refcount" is enough to track them.
181 self._refcnt = 0
182 def push(self) -> None:
183 """Binds the app context to the current context."""
184 self._refcnt += 1
185 """
186 _request_ctx_stack = LocalStack()
187 _app_ctx_stack = LocalStack()
188 详见8.3
189 """
190 _app_ctx_stack.push(self)
191 appcontext_pushed.send(self.app)
192 class LocalStack(object):
193 def __iter__(self):
194 self._local = Local()
195 def push(self, obj):
196 rv = getattr(self._local, 'stack', None)
197 if rv is None:
198 self._local.stack = rv = []
199 rv.append(obj)
200 return rv
201 def pop(self):
202 stack = getattr(self._local, 'stack', None)
203 if stack is None:
204 return None
205 elif len(stack) == 1:
206 return stack[-1]
207 else:
208 return stack.pop()
209 def top(self):
210 try:
211 return self._local.stack[-1]
212 except (AttributeError,IndexError):
213 return None
214 """
215 _request_ctx_stack = LocalStack()
216 request context,程序上下文,存储的是请求级别的信息,比如当前访问的url
217 _app_ctx_stack = LocalStack()
218 app context,应用上下文,存储的是应用级别的信息,比如数据库连接信息。
219 """
220 _request_ctx_stack = {111:{stack:[ctx,]}}
221 _app_ctx_stack = {111:{stack:[app_ctx,]}}
8.用户请求全过程
1 """
2 创建ctx=RequestContext对象,其内部封装了Request和session
3 创建app_ctx=AppContext对象,其内部封装了App和g
4 ctx.push触发ctx和app_ctx分别通过自己的LocalStack对象将其放入Local中,以线程ID为key,以{"stack":[]}为value的字典
5 {
6 1111:{"stack":[ctx,]}
7 }
8 {
9 1111:{"stack":[app_ctx,]}
10 }
11
12 注意:以后要获取request/session/app/g时,去local中获取
13 执行所有before_request
14 执行视图函数
15 执行所有after_request(session放到cookie,保存在浏览器中)
16 销毁ctx和app_ctx
17 """
原创文章,作者:,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/269097.html