flask路由、模板、请求响应、session

发布时间 2023-08-22 19:59:19作者: 星空看海

一 路由系统

# 1 flask路由系统是基于装饰器的:参数如下
# 2 转换器:
# 3 路由系统本质
# 4 endpoint 不传会怎么样,不传会以视图函数的名字作为值,但是如果加了装饰器,所有视图函数名字都是inner,就会出错,使用wrapper装饰器再装饰装饰器

1.1 flask路由系统的参数

# 1  flask路由系统是基于装饰器的:参数如下
# rule:路径
# methods :请求方式,列表
# endpoint: 路径别名

from flask import Flask

app = Flask(__name__)

@app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
def index(name):
    print(name)
    return 'hello world'

1.2 转换器(了解)

'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
# 重点:string  int  path


from flask import Flask
app = Flask(__name__)

# @app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
# @app.route('/<path:aa>', methods=['GET'])
@app.route('/<aa>', methods=['GET'])  # default可以不写,后面路径中只能带一个/
def index(name):
    print(name)
    return 'hello world'

1.3 路由系统本质-->读源码

# 1 点击route进去看源码
@app.route('/<aa>', methods=['GET'])  # 本质:index=app.route('/<aa>', methods=['GET'])(index)--->index=decorator(index)
def index(name):
    print(name)
    return 'hello world'


# 2 Scaffold类的route方法 ---> 返回decorator函数内存
class Scaffold:
	@setupmethod
    def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
        def decorator(f: T_route) -> T_route:
            endpoint = options.pop("endpoint", None)  # 从options弹出,如果没有,就是None --->@app.route(传了就有,不传就是None)
            '''核心'''
            self.add_url_rule(rule, endpoint, f, **options)
            return f  # f 就是视图函数,没有对视图函数做事情,只是在执行视图函数之前,加了点操作,调用f,还是之前的f

        return decorator
    
    
class Scaffold:
    def route(self, rule, **options):
        def decorator(f):
            endpoint = options.pop("endpoint", None)  # 从options弹出,如果没有,就是None --->@app.route(传了就有,不传就是None)
            '''核心'''
            self.add_url_rule(rule, endpoint, f, **options)
            return f  # f 就是视图函数,没有对视图函数做事情,只是在执行视图函数之前,加了点操作,调用f,还是之前的f

        return decorator
    
# 核心
'''
self.add_url_rule(rule, endpoint, f, **options)
'''
self.add_url_rule(rule, endpoint, f, **options)  # --->self就是app对象
<==> app.add_url_rule('路由地址', '路由别名', 视图函数, **options)--->跟django很像


就可以改造为:
def index():
    return 'hello world'

def home():
    return 'home'

app.add_url_rule('/', 'index', index)
app.add_url_rule('/home', 'home', home)


# add_url_rule的参数详解
    -rule, URL规则,路径地址
    -view_func, 视图函数名称
    -defaults = None, 默认值, URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
    -endpoint = None, 名称,用于反向生成URL,即: url_for('名称')
    -methods = None, 允许的请求方式,如:["GET", "POST"]
    -strict_slashes = None  # 对URL最后的 / 符号是否严格要求
    -redirect_to = None,   # 重定向到指定地址
    
    
def index(name):
    print(name)
    return 'hello world'

def home():
    return 'home'

app.add_url_rule('/', 'index', index,defaults={'name':'lqz'})  # django讲media中
app.add_url_rule('/home', 'home', home,strict_slashes=True,redirect_to = '/')

1.4 endpoint

# endpoint 不传会怎么样,不传会以视图函数的名字作为值,但是如果加了装饰器,所有视图函数名字都是inner,就会出错,使用wrapper装饰器再装饰装饰器


from flask import Flask
app = Flask(__name__)


@app.route('/', methods=['GET'])
def index(name):
    print(name)
    return 'hello world'

def home():
    return 'home'

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

1.5 flask写接口api

# 使用Flask-Restful插件:
https://www.cnblogs.com/donghaoblogs/p/10389696.html

二 CBV

1 cbv写法
	-1 写个类,继承MethodView
    -2 在类中写跟请求方式同名的方法
    -3 注册路由:app.add_url_rule('/home', view_func=Home.as_view('home'))  #home是endpoint,就是路由别名
    
2 cbv加装饰器
	-方式一:
	class Home(MethodView):
    	decorators = [auth]  # 之前加载fbv上的,直接拿过来用即可
     -方式二:
    class Home(MethodView):
        @auth  # 这个auth需要单独写,跟加载fbv上的不一样
    	def get(self):
        	return render_template('home.html')
        
        
3 允许的请求方式
    class Home(MethodView):
        methods = ['GET']  # 控制能够允许的请求方式
        
4 cbv源码

5 为什么decorators = [auth] 能加装饰器
	app.add_url_rule('/home', view_func=view内存地址)
    用装饰器一直在装饰 view内存地址 ,所以,以后执行,就是有装饰器的view,装饰器代码会走
    @auth
    def view(**kwargs):
        return self.dispatch_request(**kwargs)
    等价于
    view = auth(view)
    
    
6  view_func=Home.as_view('home')  home 是 endpoint,就是路由别名
	-1 app.add_url_rule('/home', view_func=Home.as_view('home'))
    -2 add_url_rule--->endpoint没传--->会执行endpoint = _endpoint_from_view_func(view_func)
    -3 取函数名作为 endpoint 的值
    -4 view_func是 加了一堆装饰器的view函数--->它的名字应该是装饰器的名字-->但是
    	view.__name__ = name  # name 就是home
    -5 所以endpoint就是你传的home
    -6 如果传了endpoint,就是传的那个,那as_view('home')就没作用了,但是也必须要传
# 基于类的视图

from flask import Flask, render_template

# app = Flask(__name__,template_folder='templates')
app = Flask(__name__)
app.debug = True

def auth(func):
    def inner(*args, **kwargs):
        print('我执行了')
        ret = func(*args, **kwargs)
        return ret

    return inner

# FBV:基于函数的视图
@app.route('/')
@auth
def index():
    return 'hello'

# cbv:基于类的视图    继承MethodView--->View
from flask.views import View, MethodView


# 1 写个类继承
class Home(MethodView):
    decorators = [auth]  # 如果有多个装饰器,使用  , 继续拼接即可
    methods = ['GET']  # 控制能够允许的请求方式

    def get(self):
        return render_template('home.html')

    def post(self):
        return '我是post'


# 2 注册路由
app.add_url_rule('/home', view_func=Home.as_view('home'))


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

2.2 as_view源码


# View的as_view
@classmethod
def as_view(cls, name, *class_args, **class_kwargs) :
       def view(**kwargs):
            return self.dispatch_request(**kwargs)

        if cls.decorators:   # 咱们的装饰器
            view.__name__ = name
            view.__module__ = cls.__module__
            for decorator in cls.decorators: #每次拿出一个装饰器,
                view = decorator(view) # 装饰器语法糖干的事: 把被装饰的函数当参数传入到装饰器,返回结果赋值给被装饰的函数,一个个用装饰器包装view
        view.__name__ = name

        return view
    

# self.dispatch_request--->MethodView
def dispatch_request(self, **kwargs) :
    # 取到request.method.lower()请求方式小写 --->假设是get请求get
    # meth是 cbv中 以get命名的方法,反射出来了
    meth = getattr(self, request.method.lower(), None)
    return meth(**kwargs) # 执行cbv中跟请求方式同名的方法

三 模板

# 之前dtl中学的所有知识,拿到这,都可以用--->dtl是django自己的,不能独立使用
# jinja2 模板语法,第三方,flask使用了它,它可以单独使用
# jinja2 模板语法 支持括号调用,支持 字典[] 取值---->模板中写原来python的语法都支持

# jinja2模板语法处理了xss攻击
	django,jinja2处理xss攻击原理是?
    	-使用了html特殊字符的替换,把字符串中得 <  > 都用特殊字符替换
# extends,include 
# 基于类的视图

from flask import Flask, render_template,Markup

app = Flask(__name__)
app.debug = True


@app.route('/')
def index():
    s = Markup("<input type='text' value='姓名'/>")  # 直接使用
    # s = "<input type='text' value='姓名'/>"  模板中使用{{s|safe}}
    a=10
    return render_template('index.html',s=s,a=a)


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

四 请求响应

# 所有web:请求对象,响应对象(go,java,ptyhon)
	django:request(每个请求一个request),新手四件套
    flask:requset:全局的,但是也是每个请求一个request,新手三件套
    
# flask请求对象,全局的request
    # 请求相关信息
        # request.method  请求的方法
        # request.args  get请求提交的数据
        # request.form   post请求提交的数据
        # request.values  post和get提交的数据总和
        # request.cookies  客户端所带的cookie
        # request.headers  请求头
        # request.path     不带域名,请求路径
        # request.full_path  不带域名,带参数的请求路径
        # request.url           带域名带参数的请求路径
        # request.base_url		带域名请求路径
        # request.url_root      域名
        # request.host_url		域名
        # request.host			服务端地址
        # request.files
        # obj = request.files['the_file_name']
        # obj.save('/var/www/uploads/' + secure_filename(f.filename))

    
# flask的响应
	-四件套:
    	-1 直接返回字符串
        -2 返回模板:render_template
        -3 返回重定向:redirect,
        -4返回json格式:jsonify
        
    -响应中写cookie
    	 res = make_response('home') 
          res.set_cookie('yyy', 'yyy', path='/home')
        # 删除cookie
        # res.delete_cookie('key')
    -响应头中写内容
      res = make_response('home')  # res 就是响应对象
      res.headers['xxx'] = 'xxx'
    

from flask import Flask, request, render_template, redirect, jsonify, make_response

app = Flask(__name__)
app.debug = True


@app.route('/', methods=['POST', 'GET'])
def index():
    # 在哪个里面执行代码,这个request代指的就是谁的request
    # print(request.path)
    # test()
    # print(request.values)
    # print(request.path)
    # print(request.full_path)
    print(type(request.files.get('myfiles')))
    with open('xx.zip', 'wb') as f:
        for line in request.files.get('myfiles'):
            f.write(line)
    # from werkzeug.datastructures.file_storage import FileStorage
    return 'hello'


@app.route('/home')
def home():
    # 在哪个里面执行代码,这个request代指的就是谁的request
    print(request.path)
    test()
    # return jsonify({'name': 'lqz', 'age': 19})
    # return jsonify([{'name': 'lqz', 'age': 19},{'name': 'lqz', 'age': 19}])
    # return jsonify({'code': 100, 'msg': '成功', 'data': []})

    # 往响应头写数据
    res = make_response('home')  # res 就是响应对象
    res.headers['xxx'] = 'xxx'

    # 往cookie中写数据
    res.set_cookie('yyy', 'yyy', path='/home')
    # 删除cookie
    # res.delete_cookie('key')
    return res


def test():
    print(type(request.args))
    # from werkzeug.datastructures.structures import ImmutableMultiDict
    print(request.args)  # get请求的参数


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

五 session

# cookie session token
	
# flask中得session,没有在服务端存储数据的--->后期扩展,可也把session存到redis中

# 全局session
	-放值:session['key']=value
    -取值:session.get('key')
    -删除值:session.pop('username', None)
    
    
# session的运行机制
	'''
    django
    1 生成一个随机字符串
    2 把数据保存到djagno-session表中
    3 把随机字符串返回给前端-->当cookie存到浏览器中了-->浏览器再发请求,携带cookie过来
    4 根据随机字符串去表中查--->转到request.session中
    flask
    1 把数据加密转成字符串: eyJuYW1lIjoibHF6In0.ZMnbJw.ZUceSaD0kGnU97tu9ZWm3380r00
    2 以cookie形式返回给前端--->保存到浏览器中
    3 浏览器再发请求,携带cookie过来
    4 加密符串--->解密--->放到session对象中
    '''
    
    
# 源码分析,看运行机制
	-flask默认使用:SecureCookieSessionInterface作为session的类
    -请求来了,
        -客户端带了cookie--->取出cookie 中session对应的值
        -使用解密方式对它进行解密
        -放到session对象中
    -请求走了
    	-把用户放到session中得数据
        -加密--->转成字符串-->以cookie形式返回给前端
        
    -SecureCookieSessionInterface的方法e:
    	-open_session:请去来了用
        -save_session:请求走了用
    
from flask import Flask, session

app = Flask(__name__)
app.debug = True
app.secret_key = 'asdfasdfasdasdf'
app.session_interface

@app.route('/', methods=['POST', 'GET'])
def index():
    session['name'] = 'lqz'
    session['age'] = 19
    return 'hello'


@app.route('/home')
def home():
    print(session.get('name'))
    return 'home'


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