Flask总结版2
flask介绍
轻量级Web应用框架
WSGI工具箱采用Werkzeug
模板引擎则使用Jinjia2
Flask使用BSD授权
比较Web框架
Flask:短小精悍,内部没有包含多少组件,但是第三方的组件是非常丰富的。
Django:django是一个重武器,内部包含了非常多的组件:orm,form,modelForm,缓存,session等等
Tornado:牛逼之处就是异步非阻塞框架和node.js

Flask的快速入门
创建python虚拟环境:virtualenv 虚拟名
安装:pip install flask
from werkzeug.wrappers import Request, Response
@Request.application
def hello(request):
return Response('Hello World!')
if __name__ == '__main__':
from werkzeug.serving import run_simple
run_simple('localhost', 4000, hello)
基本使用:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
wsgi:使用werkzeug模块实现的,还可以使用wsgiref实现。本质是导入socket实现的。
一旦出现这个,监听事件就开始了
实例化Flask对象
app.run():监听用户的请求,一旦有用户的请求过来,就会直接执行用户的__call__方法。
flask的所有相关的组件全都存在flask文件下的,需要什么导入什么
from flask import Flask,render_template,request,redirect,session,url_for
for_url:高级版的重定向
from flask import Flask,render_template,request,redirect,session,url_for
app = Flask(__name__)
app.debug=True
app.secret_key='fangshao'
USERS = {
1:{'name':'张桂坤','age':18,'gender':'男','text':"当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"},
2:{'name':'主城','age':28,'gender':'男','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
3:{'name':'服城','age':18,'gender':'女','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"},
}
@app.route('/')
def hello_world():
return 'Hello World!'
# @app.route('/index',methods=['GET'])
# def index():
# user=session.get('user_info')
# print(user)
# if user:
# return render_template('index.html',data=user,user_dict=USERS)
# return redirect('/login')
@app.route('/index',methods=['GET'])
def index():
user=session.get('user_info')
if user:
return render_template('index.html',data=user,user_dict=USERS)
url=url_for('111')
return redirect(url)
@app.route('/detail/<int:nid>',methods=['GET'])
def detail(nid):
user=session.get('user_info')
if not user:
return redirect('/login')
return render_template('detail.html',data=USERS[nid])
@app.route('/login',methods=['GET','POST'],endpoint='111')
def login():
if request.method=='POST':
user=request.form.get('user')
pwd=request.form.get('pwd')
if user=='fang' and pwd=='123':
session['user_info']=user
return redirect('/index')
else:
return render_template('login.html',msg='用户名或密码错误')
return render_template('login.html')
if __name__ == '__main__':
app.run()
配置文件
开放封闭原则:对代码的修改是封闭的,对配置文件的修改时开放的。
app.debug=True:修改过后自动重启项目
app.secret_key='随机设置字符串':全局设置session,Session, Cookies以及一些第三方扩展都会用到,
app.config['debug']=True:修改过后自动重启项目
app.config:获取当前app的所有配置
app.config.from_object:导入文件的一个类,
内部实现原理:导入时,将路径由点分割
配置文件的格式有:
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为: { '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, }
app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...)
app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True})
app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写
settings.py默认路径要放在当前项目的第一级目录下面
settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
路由系统
- @app.route('/user/<username>')
- @app.route('/post/<int:post_id>')
- @app.route('/post/<float:post_id>')
- @app.route('/post/<path:path>')
- @app.route('/login', methods=['GET', 'POST'])
路由比较特殊,:是基于装饰器实现的,但是究其本质还是有add_url_rule实现的。
装饰器可以有多个,放在上面和下面是不同的,
from flask import Flask,render_template,request,redirect,session,url_for app = Flask(__name__) app.debug=True app.secret_key='fangshao' USERS = { 1:{'name':'张桂坤','age':18,'gender':'男','text':"当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"}, 2:{'name':'主城','age':28,'gender':'男','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, 3:{'name':'服城','age':18,'gender':'女','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, } def get_session(func): def inner(*args,**kwargs): user = session.get('user_info') if not user: return redirect('/login') return func(*args,**kwargs) return inner @app.route('/',endpoint='1111') @get_session def hello_world(): return 'Hello World!' @app.route('/index',methods=['GET'],endpoint='n1') @get_session def index(): # user=session.get('user_info') # print(user) # if user: return render_template('index.html',user_dict=USERS) # return redirect('/login') # @app.route('/index',methods=['GET']) # def index(): # user=session.get('user_info') # if user: # return render_template('index.html',user_dict=USERS) # url=url_for('111') # return redirect(url) @app.route('/detail/<int:nid>',methods=['GET'],endpoint='n2') def detail(nid): user=session.get('user_info') if not user: return redirect('/login') return render_template('detail.html',data=USERS[nid]) @app.route('/login',methods=['GET','POST'],endpoint='111') def login(): if request.method=='POST': user=request.form.get('user') pwd=request.form.get('pwd') if user=='fang' and pwd=='123': session['user_info']=user return redirect('/index') else: return render_template('login.html',msg='用户名或密码错误') return render_template('login.html') if __name__ == '__main__': app.run()
注意:这里加上装饰器,会重名,给他设置一个别名endpoint=‘别名’
functools.wraps(函数):# 帮助我们保存一些设置函数的元信息
from flask import Flask,render_template,request,redirect,session,url_for app = Flask(__name__) app.debug=True app.secret_key='fangshao' USERS = { 1:{'name':'张桂坤','age':18,'gender':'男','text':"当眼泪掉下来的时候,是真的累了, 其实人生就是这样: 你有你的烦,我有我的难,人人都有无声的泪,人人都有难言的苦。 忘不了的昨天,忙不完的今天,想不到的明天,走不完的人生,过不完的坎坷,越不过的无奈,听不完的谎言,看不透的人心放不下的牵挂,经历不完的酸甜苦辣,这就是人生,这就是生活。"}, 2:{'name':'主城','age':28,'gender':'男','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, 3:{'name':'服城','age':18,'gender':'女','text':"高中的时候有一个同学家里穷,每顿饭都是膜膜加点水,有时候吃点咸菜,我们六科老师每天下课都叫他去办公室回答问题背诵课文,然后说太晚啦一起吃个饭,后来他考上了人大,拿到通知书的时候给每个老师磕了一个头"}, } import functools def get_session(func): @functools.wraps(func) def inner(*args,**kwargs): user = session.get('user_info') if not user: return redirect('/login') return func(*args,**kwargs) return inner @app.route('/',endpoint='1111') @get_session def hello_world(): return 'Hello World!' @app.route('/index',methods=['GET']) @get_session def index(): # user=session.get('user_info') # print(user) # if user: return render_template('index.html',user_dict=USERS) # return redirect('/login') # @app.route('/index',methods=['GET']) # def index(): # user=session.get('user_info') # if user: # return render_template('index.html',user_dict=USERS) # url=url_for('111') # return redirect(url) @app.route('/detail/<int:nid>',methods=['GET']) def detail(nid): user=session.get('user_info') if not user: return redirect('/login') return render_template('detail.html',data=USERS[nid]) @app.route('/login',methods=['GET','POST']) def login(): if request.method=='POST': user=request.form.get('user') pwd=request.form.get('pwd') if user=='fang' and pwd=='123': session['user_info']=user return redirect('/index') else: return render_template('login.html',msg='用户名或密码错误') return render_template('login.html') if __name__ == '__main__': app.run()
FBV的写法:
使用装饰器,将括号里面的内容添加到路由中@app.route()
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
methods=[]:里面写的是请求的方法,支持什么方法都要写上什么方法。
endpoint=别名:为当前的url反向生成一个url,也就是起一个别名
app.add_url_rule('/...'):添加路由的另一种方法
def auth(func): def inner(*args, **kwargs): print('before') result = func(*args, **kwargs) print('after') return result return inner @app.route('/index.html',methods=['GET','POST'],endpoint='index') @auth def index(): return 'Index' 或 def index(): return "Index" self.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) or app.add_url_rule(rule='/index.html', endpoint="index", view_func=index, methods=["GET","POST"]) app.view_functions['index'] = index
CBV的写法:
MethodView:API的简单的一种实现方式,class创建的视图类就需要继承它
app.add_url_rule('/路径',view_func='类名'.as_view(name=返回的那个函数)
app.add_url_rule('/index',view_func=IndexView.as_view(name='index'))
对于CBV来说:虽然传进去的是类名,但是最后返回的还是一个函数。
def auth(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
class IndexView(views.View):
methods = ['GET']
decorators = [auth, ]
def dispatch_request(self):
print('Index')
return 'Index!'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
或
class IndexView(views.MethodView):
methods = ['GET']
decorators = [auth, ]
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
@app.route和app.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最后的 / 符号是否严格要求,
如:
@app.route('/index',strict_slashes=False),
访问 http://www.xx.com/index/ 或 http://www.xx.com/index均可
@app.route('/index',strict_slashes=True)
仅访问 http://www.xx.com/index
redirect_to=None, 重定向到指定地址
如:
@app.route('/index/<int:nid>', redirect_to='/home/<nid>')
或
def func(adapter, nid):
return "/home/888"
@app.route('/index/<int:nid>', redirect_to=func)
subdomain=None, 子域名访问
from flask import Flask, views, url_for
app = Flask(import_name=__name__)
app.config['SERVER_NAME'] = 'wupeiqi.com:5000'
@app.route("/", subdomain="admin")
def static_index():
"""Flask supports static subdomains
This is available at static.your-domain.tld"""
return "static.your-domain.tld"
@app.route("/dynamic", subdomain="<username>")
def username_index(username):
"""Dynamic subdomains are also supported
Try going to user1.your-domain.tld/dynamic"""
return username + ".your-domain.tld"
if __name__ == '__main__':
app.run()
default:传入函数的参数。就是url后面的那个参数
subdomain={}:创建子域名
window设置域名:hosts而文件下面直接就可以设置了C:\Windows\System32\driver\etc\hosts
mcs系统设置域名:/ect/hosts文件下面就可以设置域名了
from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
模板
1、模板的使用:Flask使用的是Jinja2模板,所以其语法和Django无差别
2、自定义模板方法:Flask中自定义模板方法的方式和Bottle相似,创建一个函数并通过参数的形式传入
for循环,并取到索引
{% for k,v in 对象.items()%}
字典的取值方法:v.字段名 v['字段名'] v.get('字段名')
{% endfor %}
函数渲染:不仅要加上括号,还可以加上参数
{{函数名(参数)}} 加上|safe:防止xss攻击
Markup:后台设置xss攻击
宏定义:就是定义一块html,定义的这一块就是就是一个函数
{% macre 函数名(参数)%} {% endmacre %}
请求和响应
请求和响应都是从flask为念中导入的
request:请求
response:响应
jsonify:响应的数据类型不是字符串类型,就是用这个将响应的内容转成字符串。
Session和Cookie
session在使用前必须要有app.sceret_key加密,就相当于加盐
session['名']=字段:设置session
应用demo:实例,使用装饰器写一个用户认证
思路:装饰器,一个函数可以加上多个装饰器,反向查找的名称不允许重名:endpoint
session.pop:删除一个session
我们这里使用的session是flask内置的使用加密的cookie来保存数据的。
基本使用:
from flask import Flask, session, redirect, url_for, escape, request app = Flask(__name__) @app.route('/') def index(): if 'username' in session: return 'Logged in as %s' % escape(session['username']) return 'You are not logged in' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': session['username'] = request.form['username'] return redirect(url_for('index')) return ''' <form action="" method="post"> <p><input type=text name=username> <p><input type=submit value=Login> </form> ''' @app.route('/logout') def logout(): # remove the username from the session if it's there session.pop('username', None) return redirect(url_for('index')) # set the secret key. keep this really secret: app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
自定义session
pip3 install Flask-Session run.py from flask import Flask from flask import session from pro_flask.utils.session import MySessionInterface app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' app.session_interface = MySessionInterface() @app.route('/login.html', methods=['GET', "POST"]) def login(): print(session) session['user1'] = 'alex' session['user2'] = 'alex' del session['user2'] return "内容" if __name__ == '__main__': app.run() session.py #!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json from flask.sessions import SessionInterface from flask.sessions import SessionMixin from itsdangerous import Signer, BadSignature, want_bytes class MySession(dict, SessionMixin): def __init__(self, initial=None, sid=None): self.sid = sid self.initial = initial super(MySession, self).__init__(initial or ()) def __setitem__(self, key, value): super(MySession, self).__setitem__(key, value) def __getitem__(self, item): return super(MySession, self).__getitem__(item) def __delitem__(self, key): super(MySession, self).__delitem__(key) class MySessionInterface(SessionInterface): session_class = MySession container = {} def __init__(self): import redis self.redis = redis.Redis() def _generate_sid(self): return str(uuid.uuid4()) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='flask-session', key_derivation='hmac') def open_session(self, app, request): """ 程序刚启动时执行,需要返回一个session对象 """ sid = request.cookies.get(app.session_cookie_name) if not sid: sid = self._generate_sid() return self.session_class(sid=sid) signer = self._get_signer(app) try: sid_as_bytes = signer.unsign(sid) sid = sid_as_bytes.decode() except BadSignature: sid = self._generate_sid() return self.session_class(sid=sid) # session保存在redis中 # val = self.redis.get(sid) # session保存在内存中 val = self.container.get(sid) if val is not None: try: data = json.loads(val) return self.session_class(data, sid=sid) except: return self.session_class(sid=sid) return self.session_class(sid=sid) def save_session(self, app, session, response): """ 程序结束前执行,可以保存session中所有的值 如: 保存到resit 写入到用户cookie """ domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) val = json.dumps(dict(session)) # session保存在redis中 # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime) # session保存在内存中 self.container.setdefault(session.sid, val) session_id = self._get_signer(app).sign(want_bytes(session.sid)) response.set_cookie(app.session_cookie_name, session_id, expires=expires, httponly=httponly, domain=domain, path=path, secure=secure)
第三方
from flask import Flask, session, redirect from flask.ext.session import Session app = Flask(__name__) app.debug = True app.secret_key = 'asdfasdfasd' app.config['SESSION_TYPE'] = 'redis' from redis import Redis app.config['SESSION_REDIS'] = Redis(host='192.168.0.94',port='6379') Session(app) @app.route('/login') def login(): session['username'] = 'alex' return redirect('/index') @app.route('/index') def index(): name = session['username'] return name if __name__ == '__main__': app.run()
闪现
flash:向某一个地方设置一个值
category:设置值的分类
get_flashed_messages:从某一个地方获取多个值,并且清除
他们也是基于app.secret_key实现的
from flask import Flask,flash,get_flashed_messages app = Flask(__name__) app.secret_key = 'asdfasdf' @app.route('/get') def get(): # 从某个地方获取设置过的所有值,并清除。 data = get_flashed_messages() print(data) return 'Hello World!' @app.route('/set') def set(): # 向某个地方设置一个值 flash('阿斯蒂芬') return 'Hello World!' if __name__ == '__main__': app.run()
category_filter:只取一类的值
from flask import Flask,flash,get_flashed_messages,request,redirect app = Flask(__name__) app.secret_key = 'asdfasdf' @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: """ data = get_flashed_messages(category_filter=['x1']) if data: msg = data[0] else: msg = "..." return "错误信息:%s" %(msg,) if __name__ == '__main__': app.run()
request.query_string.get('字段'):获取到所有的url
request.args.get('字段'):获取get请求后面的值
什么是闪现:设置不管多少次的值,是基于session实现的,只要一次取到全部的值,并且清除
闪现的用法:应用于临时数据的操作,比如:显示错误信息等等
from flask import Flask,session,Session,flash,get_flashed_messages,redirect,render_template,request app = Flask(__name__) app.secret_key ='sdfsdfsdf' @app.route('/users') def users(): # 方式一 # msg = request.args.get('msg','') # 方式二 # msg = session.get('msg') # if msg: # del session['msg'] # 方式三 v = get_flashed_messages() print(v) msg = '' return render_template('users.html',msg=msg) @app.route('/useradd') def user_add(): # 在数据库中添加一条数据 # 假设添加成功,在跳转到列表页面时,显示添加成功 # 方式一 # return redirect('/users?msg=添加成功') # 方式二 # session['msg'] = '添加成功' # 方式三 flash('添加成功') return redirect('/users') if __name__ == '__main__': app.run(debug=True)
蓝图(blueprint)
什么是蓝图:功能分类,还可以定义自己的模板和路由分发。就是为了构造目录的。
蓝图的特性:不仅能将不同功能的app进行分来,并且还可以定义自己的模板路径可以实现路由的分发。可以实现每一个程序构造自己的app。
Blueprint:创建蓝图 ('蓝图名','__name__')
url_prefix:为下面所有函数的使用机上一个前缀,可以为统一的某一类加上前缀
template_folder:定义自己的模板路径(html文件勒颈)
app.register_blueprint:将蓝图注册到app
如何理解蓝图?参考下面链接
https://www.zhihu.com/question/31748237/answer/55313054
十 请求的扩展
类似于django的中间件
@app.defore_request:定制请求函数,每次请求都会执行有这个装饰器的函数,每次都是从上到下执行的
request.url:拿到正要运行的url
@app.after_request:定制响应的函数,每次响应执行这个装饰器的函数,每次都是从下到上执行的
请求哈响应可以有多个,如果请求给拦截了,但是所有的响应都会执行
@app.errorhandler:定制错误的信息。
@ app.template_global:为模板定制函数,也就是自定义模板
@app.before_first_request:只有第一次请求来了才执行这个函数
from flask import Flask,render_template,request,redirect,session,url_for app = Flask(__name__) app.debug = True app.secret_key = 'siuljskdjfs' @app.before_request def process_request1(*args,**kwargs): print('process_request1 进来了') @app.before_request def process_request2(*args,**kwargs): print('process_request2 进来了') @app.after_request def process_response1(response): print('process_response1 走了') return response @app.after_request def process_response2(response): print('process_response2 走了') return response @app.errorhandler(404) def error_404(arg): return "404错误了" @app.before_first_request def first(*args,**kwargs): pass @app.route('/index',methods=['GET']) def index(): print('index函数') return "Index" if __name__ == '__main__': app.run()
@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')
@app.template_global() def sb(a1, a2): return a1 + a2 {{sb(1,2)}} @app.template_filter() def db(a1, a2, a3): return a1 + a2 + a3 {{ 1|db(2,3)}}
中间件
中间件:在这里就是一个请求的入口
每次请求进来都会执行app的call方法。
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__': app.wsgi_app = Md(app.wsgi_app) app.run()
上下文处理
threading.local:为每一个线程开辟一个单独的内存空间来保存他自己的值
import threading # class Foo(): # def __init__(self): # self.name=0 # # local_value=Foo() local_value=threading.local() def func(num): local_value.name=num import time time.sleep(1) print(local_value.name,threading.current_thread().name) for i in range(20): th=threading.Thread(target=func,args=(i,),name='线程%s'%i) th.start()
request:
情况一:单线程和单进程的情况下不会有问题,应为自己使用为完毕过后,就会自动的清空request。
情况二:单进程和多线程,threading.local对象
情况三:但进程和单线程多个协程,theading.local对象就会出问题。
解决方法:
以后不支持协程:就可以使用内置的threading.local对象
支持协程:自定义类似threading.local对象的功能支持协程
_thread.get_ident:获取线程的一个唯一标识
greenlet.getcurrent:获取协程额唯一标识
自定义支持协程:
"""
{
1368:{}
}
"""
import threading
try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程
class Local():
def __init__(self):
self.storage={}
self.get_ident=get_ident
def set(self,k,v):
ident=self.get_ident()
origin=self.storage.get(ident)
if not origin:
origin={k:v}
else:
origin[k]=v
self.storage[ident]=origin
def get(self,k):
ident=self.get_ident()
origin=self.storage.get(ident)
if not origin:
return None
return origin.get(k,None)
local_values=Local()
def task(num):
local_values.set('name',num)
import time
time.sleep(1)
print(local_values.get('name'),threading.current_thread().name)
for i in range(20):
th=threading.Thread(target=task,args=(i,),name='线程%s'%i)
th.start()
反射实例:
class Foo(object):
def __init__(self):
object.__setattr__(self,'storage',{})
def __setattr__(self, key, value):
self.storage={'k1:v1'}
print(key,value)
def __getattr__(self, item):
print(item)
return 'df'
obj=Foo()
# obj.x = 123
print(obj)
自定义支持协程的flask:使用反射实现
"""
{
1368:{}
}
"""
import flask.globals
import threading
try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程
class Local():
def __init__(self):
object.__setattr__(self,'__storage__',{})
object.__setattr__(self,'__ident_func__',get_ident)
def __setattr__(self, name, value):
ident=self.__ident_func__()
storage=self.__storage__
try:
storage[ident][name]=value
except KeyError:
storage[ident]={name:value}
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_finc__()][name]
except KeyError:
raise AttributeError(name)
local_values=Local()
def task(num):
local_values.name=num
import time
time.sleep(1)
print(local_values.name,threading.current_thread().name)
for i in range(20):
th=threading.Thread(target=task,args=(i,),name='线程%s'%i)
th.start()

浙公网安备 33010602011771号