flask 请求与响应,session使用与源码分析,闪现与请求扩展

发布时间 2023-04-03 17:47:22作者: 李阿鸡

cbv分析

基于类的视图

cbv写法

from flask import Flask,request
# 视图基类
from flask.views import MethodView

app = Flask(__name__)

app.DEBUG = True


# 视图类,继承MethodView
class IndexView(MethodView):
    def get(self):
        print(request.method)
        return 'get'

    def post(self):
        return 'post'


app.add_url_rule('/', endpoint='index', view_func=IndexView.as_view('index'))

if __name__ == '__main__':
    app.run()

源码分析

Indexview.as_view('index') 执行完的结果,return反回了view方法
as_view(c1s,name,*class_args,**class_kwargs):
	def view(**kwargs: t.Any) -> ft.ResponseReturnValue:
       # 本质是在执行self.dispatch_request 用异步
		return current_app.ensure_sync(self.dispatch_request)(**kwargs)
   return view

# 请求来了就是在执行 MethodView里的 dispatch_request
    def dispatch_request(self, **kwargs):
        # 去self(视图类的对象) 拿到请求方法
        meth = getattr(self, request.method.lower(), None)
        if meth is None and request.method == "HEAD":
            meth = getattr(self, "get", None)
        assert meth is not None, f"Unimplemented method {request.method!r}"
        # 用异步执行meth()
        return current_app.ensure_sync(meth)(**kwargs)

总结

1 as_view 执行流程跟djagno一样
2 路径如果不传别名,别名就是函数名(endpoint)
3 视图函数加多个装饰器
# 上下顺序(放在下面)和必须传endpoint(不传其实用的都是装饰器内部的函数,所以需要传)
4 视图类必须继承MethodView,否则需要重写dispatch_request
5 视图类加装饰器:类属性decorators = [auth,]

模板

py文件

from flask import Flask, render_template,Markup

app = Flask(__name__, template_folder='templates', static_folder='static')  # 模板的路径必须是templates,因为实例化app对象时,传入的
app.debug=True


def add(a,b):
    return a+b
@app.route('/')
def index():
    a='<a href="http://www.baidu.com">点我看美女</a>'  # 不存在xss攻击,处理了xss
    a=Markup(a)  # 可以在这包,模板里就可以不用safa了
    return render_template('index.html',name='lqz',a=a,add=add) 


if __name__ == '__main__':
    app.run()

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>模板语法,static</h1>
<img src="/static/1.jpg" alt="">

<h1>模板语法,if</h1>
{% if name %}
<h1>Hello {{ name }}!</h1>
{% else %}
<h1>Hello World!</h1>
{% endif %}

<h1>模板语法,标签渲染</h1>
{{a|safe}}
{{a}}

<h1>模板语法,执行函数</h1>
{{add(4,5)}}

</body>
</html>

请求与响应

请求: 全局的request

from flask import Flask, request,make_response

app = Flask(__name__)
app.debug = True


@app.route('/',methods=['GET','POST'])
def index():
    print(request.method)    # 请求方式
    print(request.args)      # 获取get请求参数   是个字典
    print(request.form)      # 获取post请求参数
    print(request.values)    # 获取get和post请求参数
    print(request.cookies)   # 获取cookie
    print(request.headers)   # 获取请求头
    print(request.path)      # 获取根路径
    print(request.full_path) # 获取完整的请求路径 带过滤参数
    print(request.url)       # 获取请求url
    print(request.base_url)   # 获取请求的基础url
    print(request.url_root)   # 获取请求的根路径
    print(request.host_url)   # 获取请求的主机路径
    print(request.host)      # 获取请求的主机
    print(request.files)     # 获取上传的文件
    # 保存文件
    obj=request.files.get('file')
    obj.save(obj.filename)  #  obj.filename获取文件名,以文件名保存

if __name__ == '__main__':
    app.run()

响应: 新手四件套

from flask import Flask, request,make_response

app = Flask(__name__)
app.debug = True


@app.route('/',methods=['GET','POST'])
def index():
    # 响应就是四件套, render_template,redirect,return,jsonify
    # 1 . 往响应里面添加cookie,先产生一个response对象
    res = make_response('hello world')  # make_response,需要导入,返回的是一个response对象
    # 修改
    res.set_cookie('name','tank')
    # 2. 响应头中写数据
    res.headers['name']='tank'
    return res


if __name__ == '__main__':
    app.run()

session使用及源码分析

from flask import Flask, request, session,render_template,redirect

app = Flask(__name__)
app.debug = True

# 用session,必须设置secret_key
app.secret_key = '21312sadasd35'


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'GET':
        return render_template('login.html')
    else:
        name = request.form.get('name')
        pwd = request.form.get('pwd')
        # 添加session
        session['name'] = name
        return redirect('/index')


@app.route('/index', methods=['GET', 'POST'])
def index():
    return 'hello %s' % session.get('name','游客')

if __name__ == '__main__':
    app.run()

cookie与session

cookie 存在与客户端浏览器的键值对

session 存在于服务端的键值对 ,在django中放在了django_session表中

flask 把session加密后放到了cookie中,存入的是一个字符串,session发生了变化cookie里存入的字符串也会跟着变化。

session执行原理

image

session源码分析

app.session_interface 进入 ,发现配置了一个类的对象,它就是session的执行流程

 session_interface: SessionInterface = SecureCookieSessionInterface()

SecureCookieSessionInterface 类中有两个重要的方法

open_sessionsave_session

    def open_session(self, app, request):
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        # 根据config里配置的session名字取出前端传入的cookie的value值
        val = request.cookies.get(self.get_cookie_name(app))
        # 如果没有值, 狗仔一个空的session对象
        if not val:
            return self.session_class()
        max_age = int(app.permanent_session_lifetime.total_seconds())
        try:
            # 如果没过期,解密做成session对象
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
         # 若果过期了也是空的session
            return self.session_class()
 def save_session(self, app, session, response):
	`	 # session没有直接删除cookie
        if not session:
            if session.modified:
                response.delete_cookie(
                    name,
                    domain=domain,
                    path=path,
                    secure=secure,
                    samesite=samesite,
                    httponly=httponly,
                )

            return
        if session.accessed:
            response.vary.add("Cookie")

        if not self.should_set_cookie(app, session):
            return
		 # 取出过期时间,把session加密转成字符串,放到cookie中
        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))
        response.set_cookie(
            name,
            val,  
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            samesite=samesite,
        )

扩展

把session放到redis/mysql 中,需要重写个类,重写open_session与save_session 方法,已经有一个模板帮助我们写好了。 后面说

闪现

什么是闪现

flash 翻译过来的
 当次请求把 数据放入到某个位置,下一次请求,去那个位置把数据取出来,后那个位置数据就没了。

作用

跨请求保存数据

当次请求访问出现错误,被重定向到别的网页,重定向到这个网页里,向拿到元网页出现错误的信息。

django中的message框架也可以做到 不叫闪现 叫消息框架

用法

学习设置闪现与使用闪现

from flask import Flask, flash, get_flashed_messages,request

app = Flask(__name__)
app.secret_key = '21312sasdsa'

# 设置闪现
@app.route('/set_flash')
def set_flash():
    name = request.args.get('name')
    flash('%s我跳出来啦,你打我呀' % name)  # 也要加上secret_key,本质写在session里,可以设置多次会放在一个列表中
    flash('超时',category='debug')  # 分类存
    return '写入了闪现消息'

# 获取闪现
@app.route('/get_flash')
def get_flash():
    res = get_flashed_messages()  # 只会获取一次,获取完就没了
    get_flashed_messages(category_filter=['debug']) # 分类取
    print(res)
    return '获取了闪现消息'


if __name__ == '__main__':
    app.run()
    
"本质就是放在session中"

请求扩展

请求扩展中 : 在请求来或走,可以设置一些函数,到这里就会执行函数。类似与django中间件

在flask 中用请求扩展 代替django的中间件。

from flask import Flask

app = Flask(__name__)


# 请求来执行函数,可以写多个,从上到下执行,多个中间件有一个反回了,就不会执行下面的了,直接走响应回去
@app.before_request
def before_request():
    print('我先摸了她一下')


# 请求走了执行函数,可以写多个,从下到上执行
@app.after_request
def after_request(response):
    print('走了我又摸了她一下')
    return response


# 请求报错了执行函数,可以做错误日志记录
@app.teardown_request
def teardown(e):
    print(e)
    print('报错就会执行我')


# errorhandler 监听响应状态码,404报错就会执行
@app.errorhandler(404)
def error(arg):
    return '404'


@app.route('/')
def index():
    a = [1, 2]
    # print(a[4])
    return 'hello world'


if __name__ == '__main__':
    app.run()