http://www.javashuo.com/article/p-vzmjtyxq-ke.htmlhtml
昨天的做业就是,有3个视图函数,分别是/login,/student_list,/student_detail。写一个装饰器,除了/login之外,其余视图函数都要登陆才行!前端
使用session判断!django
原始代码json
from flask import Flask,render_template,sessions,request,redirect app = Flask(__name__) USER = {'username': 'xiao', 'password': "123"} @app.route("/login",methods=["POST","GET"]) def login(): if request.method == "GET": # 前端模板中使用了msg,这里就算是传递空,也要出现msg return render_template("login.html", msg="") if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") @app.route("/student_list") def student_list(): return "学生列表" @app.route("/student_detail") def student_detail(): return "学生详情" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
使用装饰器flask
from flask import Flask,render_template,session,request,redirect app = Flask(__name__) # 使用session,必须设置secret_key app.secret_key = "123asdzxc" USER = {'username': 'xiao', 'password': "123"} def auth(func): def inner(*args,**kwargs): if session.get("user"): return func(*args,**kwargs) else: return redirect("/login") return inner @app.route("/login",methods=["POST","GET"]) def login(): if request.method == "GET": # 前端模板中使用了msg,这里就算是传递空,也要出现msg return render_template("login.html", msg="") username = request.form["username"] # 获取POST请求时,FormData中的参数 # password = request.form.get("password") if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: # 设置session session["user"] = username return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") @app.route("/student_list",endpoint="student_list") @auth def student_list(): return "学生列表" @app.route("/student_detail",endpoint="student_detail") @auth def student_detail(): return "学生详情" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
重启flask,直接访问student_list浏览器
http://127.0.0.1:5000/student_list
打开浏览器工具,查看网络。它会跳转至登陆页面!缓存
输入正确的用户名和密码安全
提交以后,跳转页面服务器
注意:使用装饰器的视图函数,必需要定义endpoint参数。cookie
由于使用装饰器以后,视图函数都是inner,因此flask没法区分,路由到底指向哪个视图函数。
启动flask以后,会直接报错。endpoint参数,是给url起别名,惟一标识。能够作url反向解析!
还有一种方法,使用@functools.wraps装饰器,保留原函数信息,好比函数名。
可是不推荐使用。由于定义视图函数,自己就应该定义endpoint参数
一、使用装饰器装饰两个视图函数,代码以下
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") == "123": session["user"] = request.form.get("username") # 写入session return redirect("/") return render_template("login.html") app.run(debug=True)
启动程序有以下错误:
咱们还发现当装饰一个视图函数时能够正常运行,而装饰两个或两个以上视图函数则会报以上错误。
二、解决方式
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中的路由系统其实咱们并不陌生了,从一开始到如今都一直在应用
@app.route("/",methods = ["POST","GET"])
为何要这么用?其中的工做原理咱们知道多少吗?
一、@app.route()装饰器中的参数
若是不明白装饰器 点击这里
methods : 当前 url 地址,容许访问的请求方式
from flask import Flask,request app = Flask(__name__) @app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
访问url,注意:要带上参数id,不然报错
http://127.0.0.1:5000/info?id=1
效果以下:
endpoint:反向url地址,默认为视图函数名 (url_for)
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): print(url_for("r_info")) # /info stu_id = int(request.args["id"]) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新页面,效果同上!查看Pycharm控制台输出:
/info
注意:这并非完整的url。若是要给前端妹子,返回一个完整的URL怎么办呢?
url_for:用于反向生成url,也能够附带一些参数,好比想要完整的URL,能够设置_external为Ture:
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): stu_id = int(request.args["id"]) print(url_for("r_info", _external=True)) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新页面,效果同上!查看Pycharm控制台输出:
http://127.0.0.1:5000/info
可是还不够,参数没有啊?怎么办?再加上url参数。
注意:因为id是动态参数,因此后台获取时,要和实际的参数匹配。这里是id
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info") def student_info(): stu_id = int(request.args["id"]) print(url_for("r_info", _external=True,id=stu_id)) return f"hello kitty {stu_id}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新页面,效果同上!查看Pycharm控制台输出:
http://127.0.0.1:5000/info?id=1
这下,就很是完美了!
defaults : 视图函数的参数默认值{"nid":100}
注意:视图函数必需要设置形参nid,不然报错!
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", methods=["GET", "POST"],endpoint="r_info",defaults={"nid": 100}) def student_info(nid): # stu_id = int(request.args["nid"]) print(url_for("r_info", _external=True)) print(nid) return f"hello kitty {nid}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
访问url: http://127.0.0.1:5000/info
效果以下:
strict_slashes : url地址结尾符"/"的控制 False : 不管结尾 "/" 是否存在都可以访问 , True : 结尾必须不能是 "/"
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", strict_slashes=True) def student_info(): return "hello kitty info" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
末尾不带 "/"
末尾带 "/"
也就是说:strict_slashes = True ,表示开启路由严格匹配模式!即便末尾多了一个"/",也不容许访问!
将参数改成False
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", strict_slashes=False) def student_info(): return "hello kitty info" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
刷新页面,又能够访问了
若是多加一个s,会报404
总结:strict_slashes 用来控制末尾是否有"/",为Ture,带 "/"不容许!
为False,无论你带不带"/",均可以访问!
redirect_to : url地址重定向
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info", redirect_to="/infos") def student_info(): return "hello kitty info" @app.route("/infos") def student_infos(): return "Hello Tom infos" if __name__ == '__main__': app.run("0.0.0.0", 5000, debug=True)
访问url: http://127.0.0.1:5000/info,会发生重定向
查看浏览器工具,查看网络。它经历了2次请求!
它有什么应用场景呢?
好比你的网站域名是xx.com,后来你的网页改版了,换了新的域名qqxx.com。可是老客户还不知道你的新域名啊!怎么办呢?用重定向就能够了!
subdomain : 子域名前缀 subdomian="qq" 这样写能够获得 qq.xx.com 前提是app.config["SERVER_NAME"] = "xx.com:5000"
from flask import Flask,request,url_for app = Flask(__name__) # 必定必定必定要写端口!!!!!! app.config["SERVER_NAME"] = "xx.com:5000" @app.route("/",endpoint="r_info",subdomain="qq") def student_info(): print(url_for("r_info", _external=True,)) return "hello kitty info" if __name__ == '__main__': # 监听端口为5000,注意:要和SERVER_NAME匹配! app.run("0.0.0.0", 5000, debug=True)
注意:app.config["SERVER_NAME"] = "xx.com:5000"。
必定要加端口!必定要加端口!必定要加端口!重要的事情说三遍!!!
就是由于没有加端口,快要放弃了!google了一把,终于找到缘由了!
这里是window 10访问。必需要修改本机的Hosts文件,至于怎么修改,为啥没有权限,请自行百度!
修改Hosts文件,添加一条记录
127.0.0.1 qq.xx.com
打开cmd窗口,ping qq.xx.com,请确保返回地址是127.0.0.1
打开浏览器访问url,注意:只能是qq.xx.com访问。不能是xx.com!访问时,必定要带上端口!
http://qq.xx.com:5000/
效果以下:
关于路由目前就说这么多,以后的课程中会有关于Flask路由系统的源码剖析,再详细说明Flask路由系统的工做原理
<int:nid> 就是在url后定义一个参数接收
可是这种动态参数路由,在url_for的时候,必定要将动态参数名+参数值添加进去,不然会抛出参数错误的异常
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info/<int:nid>", endpoint="r_info") def student_info(nid): print(url_for("r_info", _external=True,nid=nid)) return f"hello kitty {nid}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': # 监听端口为5000 app.run("0.0.0.0", 5000, debug=True)
访问url,必定要带上参数,并且参数必须是数字!
查看Pycharm控制台输出:
12 <class 'int'> http://127.0.0.1:5000/info/12
若是是字符串,会报错
from flask import Flask,request,url_for app = Flask(__name__) @app.route("/info/<string:nid>", endpoint="r_info") def student_info(nid): print(nid,type(nid)) print(url_for("r_info", _external=True,nid=nid)) return f"hello kitty {nid}" # Python3.6的新特性 f"{变量名}" if __name__ == '__main__': # 监听端口为5000 app.run("0.0.0.0", 5000, debug=True)
刷新页面,就能够访问了
查看Pycharm控制台输出:
ask <class 'str'> http://127.0.0.1:5000/info/ask
参数是数字也是正常的
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的实例化对象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的咱们,能够将蓝图理解为没有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)@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.after_request自上而下依次执行,@app.after_request自下而上依次执行。与django的中间件相似,@app.after_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
二、重定义错误页面返回信息
@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错误时,会看到该函数返回的内容。