配置文件
配置方式
1.单个配置
1
flask.config[‘xxx’] = xxx
2.可以像django一样文件配置
1
2
3
4DEBUG = True
TESTING = True
DATABASE_URI='127.0.0.1:XXX'
flask.config.from_pyfile(‘某个文件setting’)3.类配置 生产环境一套、debug一套。
1
2
3
4
5
6
7
8
9
10
11
12class Config:
DEBUG = False
TESTING = False
DATABASE_URI = '127.0.0.1:XXX'
class ProductionConfig(Config):
DATABASE_URI = '127.0.0.1:XXX'
class TestingConfig(Config):
DEBUG = True
TESTING = True
DATABASE_URI = '127.0.0.1:XXX'
源码解析
就是一个字典结构,初始化加载了defaults,即Flask中的defaults
1 | class Config(dict): |
路由route
详细配置
普通匹配模式及其传参方法
1 | @app.route(‘/<username>’) 字符串 |
通过?匹配
即最常见的路由匹配方式
1 | http://127.0.0.1:5000/user/?a=1&b=2&c=3 |
自定义匹配模式(正则)
1 | from flask import url_for |
详解各参数作用
源码分析
基于装饰器形式展现本质上还是add_url_route,和django有所区别(本质还是一样)。
装饰器的route
1.route()调用
第一步返回一个装饰器decorator1
2
3
4
5
6def route(self, rule, **options):
def decorator(f):
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator2.decorator(func)
第二步@decorator 执行decorator函数,关键函数
其中执行了self.add_url_rule(rule, endpoint, f, **options)
把路由和执行函数和别名加到了一个路由关系对应表
注意
1.添加路由的本质就是执行add_url_rule
1
2
3def login():
pass
app.add_url_rule('/login', 'n2', login, methods=['get', 'post'])2.如果end_point是空的,就把view_func的名字赋给end_point
1
2
3
4
5
6
7
8
9
10
11
12@setupmethod
def add_url_rule(
self,
rule,
endpoint=None,
view_func=None,
provide_automatic_options=None,
**options
):
if endpoint is None:
endpoint = _endpoint_from_view_func(view_func)
options["endpoint"] = endpoint
生成rule
1 | rule = self.url_rule_class(rule, methods=methods, **options) |
rule参数解析
1 | 1.defaults=None |
url加入url_map
1 | self.url_map.add(rule) |
访问url的步骤
1 | # 从上下文获得当前用户的req |
template模板
自带防xss攻击
比如return给模板
<input …>会自动变为字符串不能变成input框
可以通过管道符|safe也可以通过from flask import Markup
return Markup(
<input …>)…
支持宏定义
1 | {% macro xx(name, type=’text’, value=’’) %} |
request&response
request
1 | from flask import request |
response
1 | 1.headers 类似于字典,用来存放headers |
session&cookie
session
基础使用
1 | from flask import session |
源码解析
session其实就是一个类字典对象,sessionId一般用cookie存储,
session值存储在服务器的缓存redis、mysql、文件缓存等中。
ctx.push()
1
2
3
4
5
6if 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)session_interface
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68# 默认session_interface
class SecureCookieSessionInterface(SessionInterface):
serializer = session_json_serializer
session_class = SecureCookieSession
def get_signing_serializer(self, app):
if not app.secret_key:
return None
signer_kwargs = dict(
key_derivation=self.key_derivation, digest_method=self.digest_method
)
return URLSafeTimedSerializer(
app.secret_key,
salt=self.salt,
serializer=self.serializer,
signer_kwargs=signer_kwargs,
)
def open_session(self, app, request):
s = self.get_signing_serializer(app)
if s is None:
return None
val = request.cookies.get(app.session_cookie_name)
if not val:
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
# If the session is modified to be empty, remove the cookie.
# If the session is empty, return without setting the cookie.
if not session:
if session.modified:
response.delete_cookie(
app.session_cookie_name, domain=domain, path=path
)
return
# Add a "Vary: Cookie" header if the session was accessed at all.
if session.accessed:
response.vary.add("Cookie")
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
app.session_cookie_name,
val,
expires=expires,
httponly=httponly,
domain=domain,
path=path,
secure=secure,
samesite=samesite,
)make_null_session(self.app)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32def make_null_session(self, app):
return self.null_session_class()
class NullSession(SecureCookieSession):
def _fail(self, *args, **kwargs):
raise RuntimeError(
"The session is unavailable because no secret "
"key was set. Set the secret_key on the "
"application to something unique and secret."
)
__setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail
class SecureCookieSession(CallbackDict, SessionMixin):
def __init__(self, initial=None):
def on_update(self):
self.modified = True
self.accessed = True
super(SecureCookieSession, self).__init__(initial, on_update)
def __getitem__(self, key):
self.accessed = True
return super(SecureCookieSession, self).__getitem__(key)
def get(self, key, default=None):
self.accessed = True
return super(SecureCookieSession, self).get(key, default)
def setdefault(self, key, default=None):
self.accessed = True
return super(SecureCookieSession, self).setdefault(key, default)
flask_session
config配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46config = app.config.copy()
config.setdefault('SESSION_TYPE', 'null')
config.setdefault('SESSION_PERMANENT', True)
config.setdefault('SESSION_USE_SIGNER', False)
config.setdefault('SESSION_KEY_PREFIX', 'session:')
config.setdefault('SESSION_REDIS', None)
config.setdefault('SESSION_MEMCACHED', None)
config.setdefault('SESSION_FILE_DIR',
os.path.join(os.getcwd(), 'flask_session'))
config.setdefault('SESSION_FILE_THRESHOLD', 500)
config.setdefault('SESSION_FILE_MODE', 384)
config.setdefault('SESSION_MONGODB', None)
config.setdefault('SESSION_MONGODB_DB', 'flask_session')
config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
config.setdefault('SESSION_SQLALCHEMY', None)
config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
if config['SESSION_TYPE'] == 'redis':
session_interface = RedisSessionInterface(
config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'memcached':
session_interface = MemcachedSessionInterface(
config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'filesystem':
session_interface = FileSystemSessionInterface(
config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'mongodb':
session_interface = MongoDBSessionInterface(
config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
config['SESSION_MONGODB_COLLECT'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
elif config['SESSION_TYPE'] == 'sqlalchemy':
session_interface = SqlAlchemySessionInterface(
app, config['SESSION_SQLALCHEMY'],
config['SESSION_SQLALCHEMY_TABLE'],
config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
config['SESSION_PERMANENT'])
else:
session_interface = NullSessionInterface()
return session_interface使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36from flask import Flask, session
from flask_redis import FlaskRedis
from flask_session import Session
app = Flask(__name__)
# 必须设置
secret_key = 'asfasfs'
redis = FlaskRedis()
session_store = Session()
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 设置session 的时间为7天
class Config():
# DEBUG调试模式
DEBUG = True
# json多字节转unicode编码
JSON_AS_ASCII = False
# 数据库链接配置
SECRET_KEY = "*(%#4sxcz(^(#$#8423"
# session存储方式为redis
SESSION_TYPE = "redis"
# session保存数据到redis时启用的链接对象
SESSION_REDIS = redis
# 如果设置session的生命周期是否是会话期, 为True,则关闭浏览器session就失效
SESSION_PERMANENT = True
# 是否对发送到浏览器上session的cookie值进行加密
SESSION_USE_SIGNER = True
# 保存到redis的session数的名称前缀
SESSION_KEY_PREFIX = "session:"
# redis的链接配置
REDIS_URL = "redis://localhost:6379/1"
app.config.from_object(Config)
# 初始化redis
redis.init_app(app)
# 初始化session_store
session_store.init_app(app)
session的应用
- 1.登录,做用户标识,存储一些用户的信息在session中。
- 2.购物车
session的缺点
- 1.存储在服务器,当同一时间有很多用户登录造成巨大服务器压力
- 2.会话结束session过期
- 3.不支持跨域
cookie
修改配置&参数解析
1 | 通过make_response.set_cookie设置 |
cookie的应用
1 | 1.一般用来做用户标识的键值对(第一次登录生成token) |
cookie的缺点
1 | 1.大小最大为4KB,不能存很大的数据 |
flash
原理是通过session存取,放到session的一个list里面,session基于用户已经隔离开了.
应用于对临时信息的操作
从某个地方获取设置过得所有值并清除.类似pop
1 | from flask import flash, get_flashed_messages |
请求扩展request-extension
如果一个装饰器每个视图都需要,其实就可以改为中间件实现,flask叫做请求扩展.如登录验证.
请求前before_request
1 | @app.before_request |
- 例子
1
2
3
4
5
6
7
8
9
10from flask import session, redirect
@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")
print('asdffffffff')请求后after_request
1
2
3
4@app.after_request
def process_response(resposne):
return resposne
如果request拦截,response还是全部执行,但view不执行,与django的老版中间件类似
error后errorhandler(404)
1 | @app.errorhandler(404) |
模板中定制方法template_global()
1 | @app.template_global() |
第一次请求的时候做的操作before_first_request
1 | @app.before_first_request |
中间件middleware
请求之前定制一些操作,之后也定制一些,比如打印请求log,多实例
当请求到来之后,运行run_simple(host, port, self, **options)
执行第三个参数的__call__也就是self()
- app.__call__()
1
2
3
4
5def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response) - 中间件:把wsgi_app做一个封装即可在中间加入自己的一些参数变量
1
2
3
4
5
6
7
8
9
10
11
12
13class Md:
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__":
app.wsgi_app = Md(app.wsgi_app)
app.run()
蓝图blueprint
一个程序不可能就一个py文件,flask如何实现由多个py文件来实现一个项目
普通架构思路
通过__init__把文件联系到一起
- 架构
1
2
3
4
5
6app.py
views
__init__.py
account.py
user.py
order.py - init.py
1
2
3from flask import Flask
app = Flask(__name__)
from . import account, user, order - account.py,user.py,order.py
1
2
3
4
5
6
7
8
9
10
11
12
13from . import app
@app.route('account/index')
def index():
return 'index'
@app.route('account/login')
def login():
return 'login'
@app.route('account/logout')
def logout():
return 'logout' - app.py
1
2
3
4from views import app
if __name__ == '__main__':
app.run()
蓝图使用
蓝图相当于帮我们做好了以上架构工作,并且可以指定static、template、prefix等
架构
1
2
3
4
5
6
7
8app.py
pro_flask
__init__.py
statics
templates
views
account.py
user.pyaccount.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14from flask import Blueprint, render_template
account = Blueprint('account', __name__, url_prefix='/account', template_folder='templates')
@account.route('/index1')
def index1():
return render_template('login.html')
@account.route('/login1')
def login1():
return 'login'
@account.route('/logout1')
def logout1():
return 'logout'init.py
1
2
3
4
5
6
7
8from flask import Flask
from .views.account import account
from .views.user import user
app = Flask(__name__, template_folder='templates', static_folder='statics', static_url_path='/static')
app.register_blueprint(account)
app.register_blueprint(user)app.py
1
2
3from pro_flask import app
if __name__ == '__main__':
app.run()flask专用的蓝图
1
2
3@account.before_request
def process_request(*args, **kwargs):
print('come on')