一、配置文件
1.1 默认配置
flask支持多种方式进行配置项的新增和修改,默认配置项如下。同 django框架大差不差
```python { 'DEBUG': get_debug_flag(default=False), 是否开启Debug模式 'TESTING': False, 是否开启测试模式 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, 'LOGGER_NAME': None, 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': None, 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), 'TRAP_BAD_REQUEST_ERRORS': False, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, 'JSONIFY_PRETTYPRINT_REGULAR': True, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, } 配置项的加载与使用 from flask import Flask,jsonify app=Flask(__name__) # 方式一:直接配置--->能够配的参数少 app.secret_key='asdfasdf' # secret_key 用来加密解密的密钥 app.debug=True # 修改了代码,只需要保存,自动热加载 # 方式二:通过app.config字典,配置,这个字典中放了所有的配置 app.config['DEBUG']=False # 都要大写 app.config['MYSQL_HOST'] = '127.0.0.1' # 方式三:通过settings.py 配置文件--->用得少,django 框架的使用方式 app.config.from_pyfile("settings.py") # 方式四:通过类来加载对象 class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' app.config.from_object("Config") # 方式五:通过加载json格式配置文件 app.config.from_json("settings.json") # 方式六:通过环境变量配置 app.config.from_envvar("环境变量名称") # 方式七:服务(项目)多了,配置文件多了---》配置中心 nacos 阿波罗 m={} m=request.get('url') # m的值是dict类型 app.config.from_mapping(m)
二、路由系统
@app.route(‘/detail/int:nid’,methods=[‘GET’],endpoint=‘detail’)
2.1 路由本质
django 中的路由在 urls.py 文件中进行管理 flask 中路由基于装饰器进行管理。实际上,无论是在django 还是 flask 中,只要你愿意,咋样都行
flask 注册路由的两种方式:
1. @app.router('/') #推荐使用 def index(): ... 2. app.add_url_rule('/', view_func=index) app.router() #--->本质上就是self.add_url_rule,self就是flask对象app
2.2 CBV写法
在flask 中,cbv 可以选择继承 View (django 继承的是View) 和 MethodView 两个类
如果继承 View 类,需要重写 dispatch 方法
如果继承 MethodView 类,直接写 get , post 方法即可
class HomeView(MethodView): methods = ['GET'] # 允许请求方式 decorators = [auth, ] # 加载装饰器 def get(self): print(request.path) return 'cbv的homeview'
# 添加路由 # name 是路由别名,跟endpoint一个作用,但是cbv必须传name # 也可将路由的加载放在类中 app.add_url_rule('/home',view_func=HomeView.as_view(name='home'))
2.3 路由的参数
app.add_url_rule的参数
1 rule, URL规则, 可以使用转换器 <int:pk> 2 endpoint, 当前路由的别名,如果不传, 默认已函数名作为endpoint,如果函数名重名,就会有两个 重名的地址,报错,主要用来反向解析 源码: endpoint = _endpoint_from_view_func(view_func) 用于反向生成URL,即: url_for('名称') # url_for 通过 endpoint 的值反向解析出 url 多个视图函数,如果加同一个装饰器,如果不写endpoint,就会报错 3 view_func, 视图函数名称 如果是cbv 视图类.as_view(name='xx') 4 defaults = None, 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'} 为函数提供参数,就是django中的kwargs 5 methods = None, 允许的请求方式,如:["GET", "POST"] 6 strict_slashes = None 对URL最后的 / 符号是否严格要求 7 redirect_to = None, redirect_to='/home' #重定向到指定地址 8 子域名访问 subdomain = None,
2.4 请求与响应
请求
# http请求中有的东西,都能取出来 print(request.method) # request.method 提交的方法 # request.args get请求提及的数据 print(request.args) # request.form post请求提交的数据 print(request.form) # request.values post和get提交的数据总和 print(request.values) # request.cookies 客户端所带的cookie print(request.cookies) # request.headers 请求头 print(request.headers) # request.path 不带域名,请求路径 print(request.path) # request.full_path 不带域名,带参数的请求路径 print(request.full_path) # request.url 带域名带参数的请求路径 print(request.url) # request.base_url 带域名请求路径 # request.url_root 域名 # request.host_url 域名 # request.host 127.0.0.1:500 print(request.host) # request.files 接受文件 obj = request.files['the_file_name'] obj.save('/var/www/uploads/' + secure_filename(f.filename))
响应
# 4 件套 return "字符串" # 放回字符串 return render_template('html模板路径',**{}) # 返回html文件 return redirect('/index.html') # 重定向 return jsonify({'k1':'v1'}) # json 格式字符串 # 响应头中加东西,四件套都可以使用make_response包裹成响应对象,等同于django中的HTTPResponse res=make_response('字符串') res.headers['name']='yxh' return res # 设置cookie response = make_response('字符串') response.set_cookie('key', 'value') return response
session
cookie:存放在客户端的键值对 session:存放在客户端的键值对 token:存放在客户端,通过算法来校验 在使用session之前必须现在设置一下密钥 app.secret_key="asdas" # 值随便写
除请求对象之外,还有一个 session 对象。它允许你在不同请求间存储特定用户的信息。它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥。 (app.session_interface对象)
设置:session[‘username’] = ‘xxx’
# 在django中发什么三件事,1,生成一个随机的字符串 2 往数据库存 3 写入cookie返回浏览器 # 在flask中他没有数据库,但session是怎样实现的? # 生成一个密钥写入这个cookie,然后下次请求的时候,通过这个cookie解密,然后赋值给session # 我们通过app.session_interface来查看 删除:session.pop('username', None) app.session_interface中save_session的参数(设置cookie的参数) 源码: response.set_cookie( name, val, # type: ignore expires=expires, httponly=httponly, domain=domain, path=path, secure=secure, samesite=samesite, ) name, 键 val , 值 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖) domain=None, Cookie生效的域名 你可用这个参数来构造一个跨站cookie。如, domain=".example.com"所构造的cookie对下面这些站点都是可读的:www.example.com 、 www2.example.com 和an.other.sub.domain.example.com 。如果该参数设置为 None ,cookie只能由设置它的站点读取 path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问,浏览器只会把cookie回传给带有该路径的页面,这样可以避免将cookie传给站点中的其他的应用。 secure=False, 浏览器将通过HTTPS来回传cookie samesite=None 将cookie的范围限制为仅附加到“相同站点”的请求 session源码的执行流程 处理session,有个一个类SecureCookieSessionInterface(),有俩重要方法 -save_seesion 1 把sesion对象,当字典 转成字符串,使用秘钥加密 val = self.get_signing_serializer(app).dumps(dict(session)) 2 写入cookie返回浏览器 session=加密的字符串 response.set_cookie( app.session_cookie_name, val, # 加密字符串 ) -open_session 1 根据sessionid取出加密字符串 val = request.cookies.get(app.session_cookie_name) 2 通过秘钥解密,组装成 session data = s.loads(val, max_age=max_age) self.session_class(data) 3 在视图函数中才能正常使用session['name']取值,赋值,删除值 可以自定义一个类SecureCookieSessionInterface,重写open_session和save_session,把session存到数据库,redis里
2.5 闪现(django 中 message)
-设置: flash('aaa') -取值:get_flashed_message() - -假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息 # 本质: 如果在同一次请求中,放到request对象中即可 如果在不同请求中,放到session中,所以使用闪现一定配置秘钥 # 使用 设置:flash('诱惑美女') 获取:res=get_flashed_messages()
设置: flash('美女',category='man') flash('帅哥',category='wonmen') 获取: res=get_flashed_messages(with_categories=True,category_filter=["man"]) demo @app.route('/index') def index(): # 从某个地方获取设置过的所有值,并清除。 val = request.args.get('v') if val == 'oldboy': return 'Hello World!' flash('超时错误',category="x1") return "ssdsdsdfsd" # return redirect('/error') @app.route('/error') def error(): """ 展示错误信息 :return: 如果get_flashed_messages(with_category=True) """ data = get_flashed_messages(category_filter=['x1']) if data: msg = data[0] else: msg = "..." return "错误信息:%s" %(msg,) if __name__ == '__main__': app.run()
2.6 请求扩展
和django中间件差不多的玩意,但是可以通过蓝图定义给部分接口使用
1 before_request 类比django中间件中的process_request,在请求收到之前绑定一个函数做一些事情 # 基于它做用户登录认证 @app.before_request def process_request(*args,**kwargs): if request.path == '/login': return None user = session.get('user_info') if user: return None return redirect('/login') 2 after_request 类比django中间件中的process_response,每一个请求之后绑定一个函数,如果请求没有异常 @app.after_request def process_response1(response): print('process_response1 走了') return response 3 before_first_request 第一次请求时,跟浏览器无关 @app.before_first_request def first(): pass 4 teardown_request 每一个请求之后绑定一个函数,即使遇到了异常 @app.teardown_request def ter(e): pass 5 errorhandler 路径不存在时404,服务器内部错误500 @app.errorhandler(404) def error_404(arg): return "404错误了" 6 template_global 标签 @app.template_global() def sb(a1, a2): return a1 + a2 # {{sb(1,2)}} 7 template_filter 过滤器 @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 # {{ 1|db(2,3)}} 总结: 1 重点掌握before_request和after_request, 2 注意有多个的情况,执行顺序 3 before_request请求拦截后(也就是有return值),response所有都执行
2.7 flask 中间件
flask 中间件 和 before_request 和 after_request 实现效果类似。 from flask import Flask app = Flask(__name__) @app.route('/') def index(): return 'Hello World!' # 模拟中间件 class Md(object): def __init__(self,old_wsgi_app): self.old_wsgi_app = old_wsgi_app def __call__(self, environ, start_response): print('开始之前') ret = self.old_wsgi_app(environ, start_response) print('结束之后') return ret if __name__ == '__main__': # 1我们发现当执行app.run方法的时候,最终执行run_simple,最后执行app(),也就是在执行app.__call__方法 # 2 在__call__里面,执行的是self.wsgi_app().那我们希望在执行他本身的wsgi之前做点事情。 # 3 所以我们先用Md类中__init__,保存之前的wsgi,然后我们用将app.wsgi转化成Md的对象。 # 4 那执行新的的app.wsgi_app,就是执行Md的__call__方法。 # 把原来的wsgi_app替换为自定义的, app.wsgi_app = Md(app.wsgi_app) app.run()
2.8 蓝图
视图是一个应用对请求进行响应的函数。 Flask 通过模型把进来的请求 URL 匹配到 对应的处理视图。视图返回数据, Flask 把数据变成出去的响应。 Flask 也可以反 过来,根据视图的名称和参数生成 URL 。
简单点说就是对 不同 app进行路由管理,当前蓝图下的请求扩展只对当前蓝图有效。
# 第一步:定义蓝图对象 user = Blueprint('user', __name__) # 第二步:使用蓝图写路径,写请求扩展(只针对于当前蓝图生效) @user.route('/index') # 第三步:把蓝图注册进app中 app.register_blueprint(user)
使用蓝图结构案例
目录结构:
-flask_pro -flask_test -__init__.py -static -templates -views -order.py -user.py -manage.py _init.py
from flask import Flask app=Flask(__name__) from flask_test.views import user from flask_test.views import order app.register_blueprint(user.us) app.register_blueprint(order.ord) manage.py from flask_test import app if __name__ == '__main__': app.run(port=8008) user.py from flask import Blueprint us=Blueprint('user',__name__) @us.route('/login') def login(): return 'login' order.py from flask import Blueprint ord=Blueprint('order',__name__) @ord.route('/test') def test(): return 'order test'
总结:
- xxx = Blueprint(‘account’, name,url_prefix=‘/xxx’) :蓝图URL前缀,表示url的前缀,在该蓝图下所有url都加前缀
- xxx = Blueprint(‘account’,name,url_prefix=‘/xxx’,template_folder=‘tpls’):给当前蓝图单独使用templates,向上查找,当前找不到,会找总templates
- 蓝图的befort_request,对当前蓝图有效
- 大型项目,可以模拟出类似于django中app的概念
- g对象 是global的缩写,类似于 django 框架中的 request.context 属性。目的都是为了在不污染 request 对象前提下,实现在同一个请求中,传递数据,上下文传递,赋值取值只针对于当次请求生效。
from flask import g # 设置值 g.name = "yxh" # 取值 name = g.name name = g.get("name") from flask import Flask, g app = Flask(__name__) def test_g(): print(g.get('name', None)) g.name = '李四' @app.route('/test') def test(): g.name = '张三' test_g() # 控制台会打印'张三' print(g.name) # 李四 return 'test' if __name__ == '__main__': app.run()
2.9 g对象和session的区别
session对象是可以跨request的,只要session还未失效,不同的request的请求会获取到同一个session,但是g对象不是,g对象不需要管过期时间,请求一次就g对象就改变了一次,或者重新赋值了一次