Flask(2)- 装饰器的坑及解决办法、flask中的路由/实例化配置/对象配置/蓝图/特殊装饰器(中间件、重定义错误页面)
一、装饰器的坑以及解决方法
1、使用装饰器装饰两个视图函数,代码如下
from flask import Flask, redirect, render_template, request, session
app = Flask(__name__)
app.secret_key = "wanglili" # 装饰器函数
def outer(func):
def inner(*args, **kwargs):
if session.get("user"): # 验证session
ret = func(*args, **kwargs)
return ret
else:
return redirect("/login")
return inner @app.route("/")
@outer # inner=outer(index)
def index():
return render_template("index.html") @app.route("/course")
@outer
def course():
return render_template("course.html") @app.route("/login", methods=["POST", "GET"])
def login():
if request.method == "GET":
return render_template("login.html") if request.form.get("username") == "wll"and request.form.get("password") == "":
session["user"] = request.form.get("username") # 写入session
return redirect("/") return render_template("login.html") app.run(debug=True)
启动程序有如下错误:
我们还发现当装饰一个视图函数时可以正常运行,而装饰两个或两个以上视图函数则会报以上错误。
2、解决方式
1)方式一:使用functools模块
import functools def outer(func):
@functools.wraps(func) # 保留原函数func的信息
def inner(*args, **kwargs):
if session.get("user"):
ret = func(*args, **kwargs)
return ret
else:
return redirect("/login")
return inner
2)方式二:使用flask提供的endpoint参数
@app.route("/", endpoint='index')
@outer
def index():
return render_template("index.html") @app.route("/course", endpoint='course')
@outer
def course():
return render_template("course.html")
二、flask中的路由
Flask中的路由系统我们已经见过了,并且一直在用,接下来学习几个有关路由的使用方法。
1、@app.route()装饰器中的参数
1)methods : 当前 url 地址,允许访问的请求方式,示例代码如下
@app.route("/info", methods=["GET", "POST"])
def stu_info():
return "student info"
注意:不写methods参数时默认是只允许GET请求,写methods参数时要指定所有希望的允许的请求方式,默认方式会被methods覆盖。
2)endpoint:反向url地址,默认为视图函数名,url_for利用它进行反向解析,示例代码如下
from flask import Flask, url_for
@app.route("/info", methods=["GET", "POST"], endpoint='s_info')
def stu_info():
print(url_for('s_info')) # /info
return "student info"
注意:endpoint默认为视图函数名一致,不写endpoint参数则使用视图函数名反向解析,即url_for('stu_info'),有则使用endpoint定义的名称进行反向解析。
3)defaults:视图函数的参数默认值,代码如下
@app.route("/info", methods=["GET", "POST"], defaults={"nid": 100})
def stu_info(nid):
print(nid) #
return "student info"
注意:视图函数一定要接收这个参数,且形参名称一定要与defaults参数中的key一致。
4)strict_slashes:url地址结尾符"/"的控制(False : 无论结尾"/"是否存在均可以访问 , True : 结尾必须不能是"/",即与定义的路由完全一致),示例代码如下
# 只能访问 /info
@app.route("/info", methods=["GET", "POST"], strict_slashes=True)
def stu_info():
return "student info" # 可以访问 /info 或者 /info/
@app.route("/info", methods=["GET", "POST"], strict_slashes=False)
def stu_info():
return "student info"
5)redirect_to:url地址永久重定向(对应状态码301)
# 访问地址 : /info 浏览器跳转至 /infos
@app.route("/info", redirect_to="/infos")
def student_info():
return "Hello info" @app.route("/infos")
def student_infos():
return "Hello infos"
注意:在跳转之前进行重定向,原url对应的视图函数没有执行,立即跳转到新的url。且使用一次后对浏览器会有缓存。
6)subdomain:子域名前缀 subdomian="car" 这样写可以得到 car.oldboy.com 前提是app.config["SERVER_NAME"] = "oldboy.com"
app.config["SERVER_NAME"] = "oldboy.com" @app.route("/info",subdomain="car")
def student_info():
return "Hello info"
# 访问地址为: car.oldboy.com/info
2、动态路由参数
from flask import Flask, url_for
# 访问地址 : http://127.0.0.1:5000/info/1 可以
# 访问地址:http://127.0.0.1:5000/info/e 报错
@app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="s_info")
def student_info(nid):
print(nid, type(nid)) # 1 <class 'int'>
print(url_for("s_info", nid=nid)) # /info/1
print(url_for("s_info", nid=2)) # /info/2
return "Hello info" # 访问地址 : http://127.0.0.1:5000/info/1 可以
# 访问地址 : http://127.0.0.1:5000/info/e 可以
@app.route("/info/<string:nid>", methods=["GET", "POST"], endpoint="s_info")
def student_info(nid):
print(nid, type(nid)) # 1 <class 'str'>
return "Hello info" # 访问地址 : http://127.0.0.1:5000/info/1 可以
# 访问地址 : http://127.0.0.1:5000/info/e 可以
@app.route("/info/<nid>", methods=["GET", "POST"], endpoint="s_info")
def student_info(nid):
print(nid, type(nid)) # 1 <class 'str'>
return "Hello info"
通过以上示例发现,<int:nid>就是在url后定义一个参数接收url中的参数,通过演示,还发现,当定义int类型时只能传数字,当定义string类型时可以接收数字和字符串,当不定义类型时,默认是string类型。
注意:这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去,否则会抛出参数错误的异常。
三、flask实例化配置
app = Flask(__name__) # Flask的实例化对象app
实例化对象时的参数如下:
template_folder="temp" 默认模板存放目录 templates
static_folder="static" 默认静态文件存放目录 static
static_url_path="/static" 访问静态文件路由地址,默认是"/"+static_folder
static_host=None 指定静态文件服务器地址
host_matching = False 若不是特别需要时,慎用,否则所有的route 都需要host=""的参数
subdomain_matching = False 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里
instance_path = None 指向另一个Flask实例的路径
instance_relative_config = False 是否加载另一个实例的配置
root_path = None 主模块所在的目录的绝对路径,默认项目目录
四、flask对象配置
Flask对象即Flask的实例化对象app,Flask的对象配置就是在 app.config 中添加键值对,例如,app.config["SECRET_KEY"]="/wrwfgs",但是你存进去的键必须是config中应该存在的,如果不存在的话,它会默认无用,所以我们来看一下config中有哪些key以及对应的作用?
'DEBUG': False, # 是否开启Debug模式
'TESTING': False, # 是否开启测试模式
'SECRET_KEY': None # 在启用Flask内置Session的时候/开启flash,一定要有它
'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天
'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字 'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一两句话说不清楚,一般不用它
'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它
'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天
'USE_X_SENDFILE': False, # 是否弃用 x_sendfile
'LOGGER_NAME': None, # 日志记录器的名称
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None, # 服务访问域名
'APPLICATION_ROOT': None, # 项目的完整路径
'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字
'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中
'SESSION_COOKIE_PATH': None, # cookies的路径
'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志,
'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志
'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新
'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限
'TRAP_BAD_REQUEST_ERRORS': False,
# 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
# 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
'TRAP_HTTP_EXCEPTIONS': False,
# Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
# 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
# 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
'EXPLAIN_TEMPLATE_LOADING': False, # 如果这个值被设置为True,你只会得到常规的回溯。
'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
'JSON_AS_ASCII': True,
# 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
# Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
# 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
'JSON_SORT_KEYS': True,
# 默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
# 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
# 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
以上这些key都可以被改写,当然它们也有默认值,如果没有特殊情况,不要改写它的默认值。
除了单独修改某一项的配置外,还可以通过类的方式导入配置:
首先定义一个flask_settings.py,内容如下:
class FlaskDebug(object): # debug模式下的类
DEBUG=True
SECRET_KEY="DEBUGmoshidesecret_key"
PERMANENT_SESSION_LIFETIME = 7
SESSION_COOKIE_NAME = "debug_session"
然后再flask的启动文件中使用,如下:
from flask import Flask
app = Flask(__name__)
app.config.from_object(flask_settings.FlaskDebug) # 配置debug模式
五、初识flask蓝图(Blueprint)
作为初学flask的我们,可以将蓝图理解为没有run方法的Flask对象,接下来学习如何创建以及配置蓝图。
首先创建一个目录flask_project/app01,在app01目录中创建一个views.py文件,内容如下:
from flask import Blueprint # 从flask中导入蓝图 bp1 = Blueprint("bp1", __name__) # 实例化一个蓝图对象 @bp1.route('/stuList') # 添加路由并定义视图函数
def stulist():
return "student list"
在flask_project目录中创建manage.py文件,内容如下:
from flask import Flask
from app01 import views # 导入刚写好的蓝图所在模块 app = Flask(__name__) app.register_blueprint(views.bp1) # 在Flask对象中注册蓝图对象,重要 if __name__ == '__main__':
app.run(debug=True)
此时,运行manage.py文件启动服务,然后访问http://127.0.0.1:5000/stuList,即可看到返回结果。
上述蓝图的视图函数返回了一个HttpResponse对象,那么是否可以返回模板页面呢?当然可以,实例化蓝图(Blueprint)对象时,也可以配置蓝图的模板目录(template_folder参数)和静态文件目录(static_folder参数),也就说蓝图的对象配置与Flask的对象配置方法一样,在此不过多介绍。
需要特别说明的是蓝图可以指定url前缀(url_prefix参数),方法如下:
方法一:实例化蓝图对象时指定url前缀
bp1 = Blueprint("bp1", __name__, url_prefix='/app01')
方法二:在注册蓝图时指定url前缀
app.register_blueprint(views.bp1, url_prefix='/app01')
此时,浏览器要访问地址http://127.0.0.1:5000/app01/stuList
六、flask特殊装饰器
1、flask的中间件
1)@app.before_request # 请求进入视图函数之前,类似于django中间件的request_process
2)@app.after_request # 响应返回客户端之前,类似于django中间件的response_process
manage.py代码如下:
from flask import Flask app = Flask(__name__) @app.before_request
def be1():
print('我是be1') @app.before_request
def be2():
print('我是be2') @app.before_request
def be3():
print('我是be3') @app.after_request
def af1(response):
print('我是af1')
return response @app.after_request
def af2(response):
print('我是af2')
return response @app.after_request
def af3(response):
print('我是af3')
return response @app.route('/index')
def index():
print('我是视图函数')
return 'ok' if __name__ == '__main__':
app.run(debug=True)
启动项目,访问http://127.0.0.1:5000/index,打印结果如下:
我是be1
我是be2
我是be3
我是视图函数
我是af3
我是af2
我是af1
分析:@app.befor_request自上而下依次执行,@app.after_request自下而上依次执行。与django的中间件类似,@app.before_request装饰的函数不写return或者returnNone表示放行,顺利通过,否则拦截请求,返回响应。@app.after_request装饰的函数一定要有返回值,且必须有参数接收response对象。
修改be1函数为如下代码:
@app.before_request
def be2():
print('我是be2')
return 'error'
执行结果如下:
我是be1
我是af3
我是af2
我是af1
总结:
正常情况下流程:be1 - be2 - be3 - af3 - af2 - af1
异常(be1函数发生异常)情况下流程:be1 - af3 - af2 - af1
2、重定义错误页面返回信息
@app.errorhandler(404)# 参数是错误代码
def error404(error_info):# 注意,一定要加参数接收错误信息
print(error_info)
# 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
return '页面不存在'# 可以返回 三剑客 + 小儿子
当访问时发生404错误时,会看到该函数返回的内容。
Flask(2)- 装饰器的坑及解决办法、flask中的路由/实例化配置/对象配置/蓝图/特殊装饰器(中间件、重定义错误页面)的更多相关文章
- 对于AttributeError: 'Flask' object has no attribute 'cli'的解决办法
版权声明:本文为博主原创文章,未经博主允许不得转载. 环境flask-script2.0.5.flask0.10.1 运行官方文档sample 出现问题 c:\kk\flask\examples\fl ...
- github + SourceTree管理自己的库并上传到cocoapods及各种坑的解决办法
一.上传写好的库到github(我这里使用SourceTree客户端) 1.在github上创建一个仓库 2.将仓库拉倒本地 复制仓库地址 将刚才复制的地址粘贴到这里 3.上传项目到github 将写 ...
- Win10重命名文件夹导致资源管理器卡顿的解决办法
我本机使用的是 Win10 1607,不清楚是因为什么原因导致重命名文件夹时资源管理器会被卡死,找了很长时间终于找到了解决办法,现在我把步骤粘出来以便后续遇到相同问题的朋友能及时解决. 其实操作很简单 ...
- zookeeper集群查看状态时报错Error contacting service. It is probably not running的一些坑以及解决办法
最近在搭建mq集群时候需要用到,zookeeper,可是启动的时候显示成功了,查看状态的时候却报错了: 碰到这个问题也是研究好好半天才解决,这里就总结出一个快速解决办法! 首先,必须看日志: 报错信息 ...
- 记一次开发过程中,iview遇到的一些坑以及解决办法
写在开头:本次项目采用的是vue2.0+iview3.0,最近公司没啥事,来总结一下开发过程中遇到的问题. 1.Modal关闭问题 需求背景:modal框里面是个form表单,点击确定之后,先验证fo ...
- 我遇到移动端ios系统遇到的一些坑和解决办法
我是作为一个H5移动端开发.主要是做跨平台兼容ios系统和Android系统. 在移动端中,最让我头疼的不是功能,不是业务逻辑.而是适配.俗话说:移动端适配是最头疼的事情,也是头发掉得最快的时候. 我 ...
- 微信支付之扫码支付开发:我遇到的坑及解决办法(附:Ecshop 微信支付插件)
前段时间帮一个朋友的基于ecshop开发的商城加入微信扫描支付功能,本以为是很简单的事儿——下载官方sdk或开发帮助文档,按着里面的做就ok了,谁知折腾了两三天的时间才算搞定,中间也带着疑问在网上找了 ...
- 项目集成ReactiveCocoa遇到的坑及解决办法
首先,使用CocoaPods集成(注意:由于ReactiveCocoa需要iOS8.0,并且是与swift混编的,所以Podfile文件要写成platform :ios, '8.0' 和 use_fr ...
- 使用mysql innodb 使用5.7的json类型遇到的坑和解决办法
---------------------------------------------- #查询JSON的某个字段 select data -> '$.Host' from temp #创建 ...
随机推荐
- 转-subl配置全栈开发环境
为 Sublime Text 3 设置 Python 的全栈开发环境 Sublime Text 3 (ST3) 是一个轻量级的跨平台文字编辑器,尤以其轻快的速度,易用性和强大的社区支持而著称.它一经面 ...
- JS面试题目
哪些地方会出现css阻塞,哪些地方会出现js阻塞? js的阻塞特性: 所有浏览器在下载JS的时候,会阻止一切其他活动,比如其他资源的下载,内容的呈现等等.直到JS下载.解析.执行完毕后才开始继续并行下 ...
- oracle中查看正在运行的并行进程
select count(*) from v$px_process a where a.STATUS='IN USE';
- Freemarker自定义方法
在项目中有一个需求,每个物品有一个guid,存在数据库中,而在页面上需要显示一个对应的业务数据值,暂且叫做serverId,serverId是通过guid移位计算得来.serverId只需要显示,后台 ...
- bzoj2005 能量采集 莫比乌斯或者普通容斥
/** 题目:bzoj2005 能量采集 链接:https://vjudge.net/contest/178455#problem/F 题意:栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可 ...
- js 数组取出最大值最小值和平均值的方法
1.直接遍历数组 ,,,,,,,]; ]; ;i<arr.length;i++){ if(max<arr[i]) max=arr[i]; } 2.借用Math的方法 ,,,,,,,]; v ...
- JVM参数MetaspaceSize的误解
前言 昨天谢照东大神在群里提出一个问题:怎么查看Metaspace里具体包含的是什么,起因是他的某个服务设置了-XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=5 ...
- HttpClient+jsoup登录+解析 163邮箱
找了几个,只有这个靠谱,用的是httpclient4,另外还需要commons-lang和jsoup包 http://jsoup.org/ http://www.oschina.net/code/sn ...
- CI 框架购物车问题
因为CI 是外国的框架.购物逻辑和中国的不一样.所以需要改进ci 框架的 cart 类: (1)先把 cart类拷贝一份到application/libaries/下 (2)因为cart中购车中的商品 ...
- 解决 Ubuntu 13.04 无法调节屏幕亮度的问题
13.04与12.04大部分步骤是相同的,只是12.04的方法在13.04中失败了,所以还是有必要说明一下.我到电脑是宏碁AS4750G,硬盘安装系统后电源亮度无法调节. 解决方法如下: 终端输入代码 ...