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. def __iter__(self):
  6. self.start_response('200 OK', [('Content-type', 'text/plain')])
  7. 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. def __call__(self, environ, start_response):
  20. return self.wsgi_app(environ, start_response)

本质

  1. from werkzeug.serving import run_simple
  2. from werkzeug.wrappers import BaseResponse
  3. def func(environ,start_response):
  4. print('请求来了')
  5. response = BaseResponse('你好')
  6. return response(environ,start_response)
  7. if __name__ == '__main__':
  8. run_simple('127.0.0.1',5000,func)

3.flask的快速使用

  1. from flask import Flask, render_template, jsonify, request, redirect, url_for, session
  2. # template_folder模板目录 static_folder 静态目录 static_url_path 改变url的path
  3. app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static')
  4. # app密钥
  5. app.secret_key = 's56h5h45h54s5sfs545agd6'
  6. # 登录认证
  7. def auth(func):
  8. @functools.wraps(func)
  9. def inner(*args, **kwargs):
  10. username = session.get('xxx')
  11. if not username:
  12. # redirect 重定向跳转 url_for 反向生成 等价于redirect('/login')
  13. return redirect(url_for('login'))
  14. return func(*args, **kwargs)
  15. return inner
  16. # 请求之前中间件
  17. @app.before_request
  18. def f_before():
  19. pass
  20. # 请求之后中间件
  21. @app.after_request
  22. def f_after(response):
  23. return response
  24. # 在对应用程序实例的第一个请求之前注册要运行的函数, 只会执行一次
  25. @app.before_first_request
  26. def bfr():
  27. pass
  28. # 主页 endpoint起别名
  29. @app.route('/', methods=['GET'], endpoint='index')
  30. @auth
  31. def index():
  32. DATA_DICT = {}
  33. return render_template('index.html', data_dict=DATA_DICT)
  34. # 登录 endpoint默认等于'/login'
  35. @app.route('/login', methods=['GET', 'POST'])
  36. def login():
  37. if request.method == 'GET':
  38. # return 'hello' # 返回HttpResponse响应
  39. # return jsonify({'code':1000,'data':[1,2,3]}) # 返回json数据
  40. return render_template('login.html')
  41. user = request.form.get('user', '0')
  42. if user == '1':
  43. session['xxx'] = 'yyy'
  44. return redirect('/index')
  45. return render_template('login.html', error='error')
  46. # 路径传参
  47. @app.route('/delete/<nid>', methods=['GET'])
  48. @auth
  49. def delete(nid):
  50. print(nid)
  51. return redirect(url_for('idx'))
  52. # 启动flask
  53. if __name__ == '__main__':
  54. 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
"""