1.什么是wsgi
wsgi
wsgi是一种规范.全称Python Web Server Gateway Interface,指定了web服务器和Python web应用或web框架之间的标准接口.WSGI规定,Web程序必须有一个可调用对象,且该可调用对象接收两个参数,返回一个可迭代对象:
environ:字典,包含请求的所有信息
start_response:在可调用对象中调用的函数,用来发起响应,参数包括状态码,headers等
uwsgi
uWSGI
uWSGI:是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等.
符合wsgi的application
def application(environ,srart_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['This is a python application!']
class ApplicationClass(object):
def __init__(self, environ, start_response):
self.environ = environ
self.start_response = start_response
def __iter__(self):
self.start_response('200 OK', [('Content-type', 'text/plain')])
yield "Hello world!n"
2.什么是werkzug
werkzeug
werkzeug是一个WSGI工具包,提供了Request和Response.
flask中的werkzeug
Flask中的程序实例app就是一个可调用对象,我们创建app实例时所调用的Flask类实现了call**方法,call**方法调用了wsgi_app()方法,该方法完成了请求和响应的处理,WSGI服务器通过调用该方法传入请求数据,获取返回数据
def wsgi_app(self, environ, start_response):
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
本质
from werkzeug.serving import run_simple
from werkzeug.wrappers import BaseResponse
def func(environ,start_response):
print('请求来了')
response = BaseResponse('你好')
return response(environ,start_response)
if __name__ == '__main__':
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.蓝图及其他
from flask import Blueprint, render_template, request, redirect, session, url_for
loginroute = Blueprint('loginroute', __name__)
@loginroute.route('/login', methods=['GET', 'POST'], endpoint='login')
def login():
return render_template('login.html', error=error)
# 主app中注册蓝图
def create_app():
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
app.secret_key = 'df2hfdghjdf5gj'
app.register_blueprint(login.loginroute, url_prefix='/blog')
return app
class UserView(views.MethodView):
methods = ['GET', 'POST']
decorators = [test, ]
def get(self):
return 'get'
def post(self):
return 'POST'
app.add_url_rule('/user',view_func=UserView.as_view('user'))
# 如果不用方法视图实现需要在普通视图内部调用request.method判断是否为GET,POST进行判断
5.threading.local
# 自定义threading_local()
# storage = {
# 1001:{'x1':1},
# 1002:{'x1':2},
# 1003:{'x1':3},
# 1004:{'x1':4},
# }
class Local(object):
def __init__(self):
# storage = {}
object.__setattr__(self, 'storage', {})
def __setattr__(self, key, value):
ident = threading.get_ident()
if ident in self.storage:
self.storage[ident][key] = value
else:
self.storage[ident] = {key: value}
def __getattr__(self, item):
ident = threading.get_ident()
if ident not in self.storage:
return
return self.storage[ident].get(item)
# 自定义高级threading_local()
# storage = {
# 1001:{'x1':[1,2]},
# 1002:{'x1':[]},
# 1003:{'x1':[3,]},
# 1004:{'x1':[4,5,6]},
# }
class Local(object):
def __init__(self):
# storage = {}
object.__setattr__(self, 'storage', {})
def __setattr__(self, key, value):
ident = threading.get_ident()
if ident in self.storage:
self.storage[ident][key].append(value)
else:
self.storage[ident] = {key: [value,]}
def __getattr__(self, item):
ident = threading.get_ident()
if ident not in self.storage:
return
return self.storage[ident][item][-1]
6.LocalStack和Local对象实现栈
"""
__storage__={
1111:{"stack":[xx,yy]}
}
"""
class LocalStack(object):
def __iter__(self):
self._local = Local()
def push(self, obj):
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
return stack[-1]
else:
return stack.pop()
def top(self):
try:
return self._local.stack[-1]
except (AttributeError,IndexError):
return None
7.Flask全过程
app = Flask(__name__,static_url_path='/xx')
# 在源码种生成如下数据
url_rule_class = Rule
url_map_class = Map
session interface = SecureCookieSessionInterface()
# Flask中的__init__
self.static_url_path = static_url_path
self.config = Config
self.view_functions = {
"index":index
}
self.before_request_funcs = {}
self.after_request_funcs = {}
self.before_first_request = []
self.url_map = self.url_map_class()
self.add_url_rule("static")
# 从字典中读取配置信息
app.config.from_object("xx.xx")
@app.before_request
def f_before():
pass
# 将f_before添加到self.before_request_funcs
self.before_request_funcs['None'].append(f_before)
@app.after_request
def f_after(response):
return response
# 将f_after添加到self.after_request_funcs
self.after_request_funcs['None'].append(f_after)
# self.after_request_funcs中的中间件逆序执行
@app.before_first_request
def bfr():
pass
# bfr.before_first_request_funcs
self.before_first_request_funcs['None'].append(bfr)
@app.route('/', methods=['GET'], endpoint='index')
@auth
def index():
DATA_DICT = {}
return render_template('index.html', data_dict=DATA_DICT)
#
self.add_url_rule
# 将url和对应的视图函数名绑定到rule,再将rule和endpoint放到Map,通过as.view方法实现view_func函数的传入
# Rule
endpoint和url
# Map
__init__
self.rules = [rule,rule]
# 启动flask
if __name__ == '__main__':
app.run()
# 请求到来之后,执行__call__方法
# run_simple(*,*,self,*)
# 执行第三个函数(self)
def wsgi_app(self, environ, start_response):
"""
创建ctx,包含request=Request(environ) session=None
详见8.1
"""
ctx = self.request_context(environ)
error = None
try:
try:
"""
app_ctx = AppContext(app,g)
将app_ctx放入local中
将ctx放入local
session赋值
路由匹配
详见8.1
"""
ctx.push()
"""
详见8.4
"""
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def full_dispatch_request(self) -> Response:
# 触发所有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)
return self.finalize_request(rv)
def try_trigger_before_first_request_functions(self) -> None:
if self._got_first_request:
return
with self._before_request_lock:
if self._got_first_request:
return
for func in self.before_first_request_funcs:
self.ensure_sync(func)()
self._got_first_request = True
def preprocess_request(self) -> t.Optional[ResponseReturnValue]:
names = (None, *reversed(request.blueprints))
for name in names:
if name in self.url_value_preprocessors:
for url_func in self.url_value_preprocessors[name]:
url_func(request.endpoint, request.view_args)
for name in names:
if name in self.before_request_funcs:
for before_func in self.before_request_funcs[name]:
rv = self.ensure_sync(before_func)()
if rv is not None:
return rv
return None
class RequestContext:
def __init__(
self,
app: "Flask",
environ: dict,
request: t.Optional["Request"] = None,
session: t.Optional["SessionMixin"] = None,
) -> None:
self.app = app
if request is None:
request = app.request_class(environ)
self.request = request
self.url_adapter = None
try:
self.url_adapter = app.create_url_adapter(self.request)
except HTTPException as e:
self.request.routing_exception = e
self.flashes = None
self.session = session
def push(self) -> None:
"""
调用了如下函数,返回了AppContext
详见8.2
def app_context(self) -> AppContext:
return AppContext(self)
"""
app_ctx = self.app.app_context()
app_ctx.push()
"""
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
详见8.3
"""
_request_ctx_stack.push(self)
if self.session is None:
session_interface = self.app.session_interface
self.session = session_interface.open_session(self.app, self.request)
if self.session is None:
self.session = session_interface.make_null_session(self.app)
if self.url_adapter is not None:
self.match_request()
class AppContext:
def __init__(self, app: "Flask") -> None:
self.app = app
self.url_adapter = app.create_url_adapter(None)
self.g = app.app_ctx_globals_class()
# Like request context, app contexts can be pushed multiple times
# but there a basic "refcount" is enough to track them.
self._refcnt = 0
def push(self) -> None:
"""Binds the app context to the current context."""
self._refcnt += 1
"""
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
详见8.3
"""
_app_ctx_stack.push(self)
appcontext_pushed.send(self.app)
class LocalStack(object):
def __iter__(self):
self._local = Local()
def push(self, obj):
rv = getattr(self._local, 'stack', None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
stack = getattr(self._local, 'stack', None)
if stack is None:
return None
elif len(stack) == 1:
return stack[-1]
else:
return stack.pop()
def top(self):
try:
return self._local.stack[-1]
except (AttributeError,IndexError):
return None
"""
_request_ctx_stack = LocalStack()
request context,程序上下文,存储的是请求级别的信息,比如当前访问的url
_app_ctx_stack = LocalStack()
app context,应用上下文,存储的是应用级别的信息,比如数据库连接信息。
"""
_request_ctx_stack = {111:{stack:[ctx,]}}
_app_ctx_stack = {111:{stack:[app_ctx,]}}
8.用户请求全过程
"""
创建ctx=RequestContext对象,其内部封装了Request和session
创建app_ctx=AppContext对象,其内部封装了App和g
ctx.push触发ctx和app_ctx分别通过自己的LocalStack对象将其放入Local中,以线程ID为key,以{"stack":[]}为value的字典
{
1111:{"stack":[ctx,]}
}
{
1111:{"stack":[app_ctx,]}
}
注意:以后要获取request/session/app/g时,去local中获取
执行所有before_request
执行视图函数
执行所有after_request(session放到cookie,保存在浏览器中)
销毁ctx和app_ctx
"""