一. Python 现阶段三大主流Web框架 Django Tornado Flask 对比
1.Django 主要特点是大而全,集成了很多组件,例如: Models Admin Form 等等, 不管你用得到用不到,反正它全都有,属于全能型框架
2.Tornado 主要特点是原生异步非阻塞,在IO密集型应用和多任务处理上占据绝对性的优势,属于专注型框架
3.Flask 主要特点小而轻,原生组件几乎为0, 三方提供的组件请参考Django 非常全面,属于短小精悍型框架
Django 通常用于大型Web应用由于内置组件足够强大所以使用Django开发可以一气呵成
Tornado 通常用于API后端应用,游戏服务后台,其内部实现的异步非阻塞真是稳得一批
Flask 通常应用于小型应用和快速构建应用,其强大的三方库,足以支撑一个大型的Web应用
Django 优点是大而全,缺点也就暴露出来了,这么多的资源一次性全部加载,肯定会造成一部分的资源浪费
Tornado 优点是异步,缺点是干净,连个Session都不支持
Flask 优点是精悍简单,缺点可能就是你不会!
Flask打印我们万能的HelloWorld
pip install Flask
Flask中的HttpResponse Render Redirect
HttpResponse
在Flask 中的HttpResponse 在我们看来其实就是直接返回字符串
Redirect
render(render_template)
Flask里面的render对应的是render_template
后面直接跟html文件名,只要改html文件建在同目录下的templates文件夹中,会自动匹配
Flask中的request对象
methods=["POST"] 代表这个url地址对应的视图函数只允许 POST 请求,是个列表也就是意味着可以允许多重请求方式,例如GET之类的
request.method
与Django里面的方法一致,获取本次请求的请求方式
request.form
获取前端用form表单提交过来的数据
print(request.form) # ImmutableMultiDict([(‘user‘, ‘Oldboy‘), (‘pwd‘, ‘DragonFire‘)]) # ImmutableMultiDict 它看起来像是的Dict 就用Dict的方法取值试一下吧 print(request.form["user"]) # Oldboy print(request.form.get("pwd")) # DragonFire # 看来全部才对了, ImmutableMultiDict 似乎就是个字典,再来玩一玩它 print(list(request.form.keys())) # [‘user‘, ‘pwd‘] 看来是又才对了
request.args
获取url中传递过来的所有参数值
然后会在控制台中看到 ImmutableMultiDict([(‘id‘, ‘1‘), (‘age‘, ‘20‘)]),获取到的数据直接当作字典进行操作即可
request.args 与 request.form 的区别就是:
request.args 是获取url中的参数
request.form 是获取form表单中的参数
request.values
只要是参数,无论是post过来的还是get过来我全部接受
print(request.values) # CombinedMultiDict([ImmutableMultiDict([(‘id‘, ‘1‘), (‘age‘, ‘20‘)]), ImmutableMultiDict([(‘user‘, ‘Oldboy‘), (‘pwd‘, ‘DragonFire‘)])]) print(request.values.get("id")) # 1 print(request.values["user"]) # Oldboy # 这回喜欢直接操作字典的小伙伴们有惊喜了! to_dict() 方法可以直接将我们的参数全部转为字典形式 print(request.values.to_dict()) # {‘user‘: ‘Oldboy‘, ‘pwd‘: ‘DragonFire‘, ‘id‘: ‘1‘, ‘age‘: ‘20‘}
同样获取到的数据直接当成一个大字典进行操作即可
注意啦!注意啦!
# 注意这里的坑来啦! 坑来啦! # 如果url和form中的Key重名的话,form中的同名的key中value会被url中的value覆盖 # http://127.0.0.1:5000/req?id=1&user=20 print(request.values.to_dict()) # {‘user‘: 20 ‘pwd‘: ‘DragonFire‘, ‘id‘: ‘1‘}
request.headres
用来获取本次请求的请求头
print(type(request.headers)) """ Host: 127.0.0.1:5000 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2 Accept-Encoding: gzip, deflate Referer: http://127.0.0.1:5000/home Content-Type: application/x-www-form-urlencoded Content-Length: 26 Cookie: csrftoken=vDIozqveCEfArdYXlM6goHVlSQEn7h4bDygNphL2Feas60DiM2di0jlqKfxo7xhA Connection: keep-alive Upgrade-Insecure-Requests: 1 Cache-Control: max-age=0 """
获取请求头
request.data
没有加请求头的数据都可以在这个里面找到,并且是bytes类型
request.files
如果遇到文件上传的话,request.files 里面存的是你上传的文件,但是 Flask 在这个文件的操作中加了一定的封装,让操作变得极为简单
my_file = request.files["file"] my_file.save("test.txt") # 保存文件,里面可以写完整路径+文件名
这样我们就成功的保存了一个名叫 "test.txt" 的文件了,操作还是很简单的
request.获取各种路径
# 获取当前的url路径 print(request.path)# /req # 当前url路径的上一级路径 print(request.script_root) # # 当前url的全部路径 print(request.url) # http://127.0.0.1:5000/req # 当前url的路径的上一级全部路径 print(request.url_root ) # http://127.0.0.1:5000/
request.json
如果在请求中写入了 "application/json" 使用 request.json 则返回json解析数据, 否则返回 None
Flask中的模板语言Jinja2以及render_template的深度用法
Flask对Jinja2稍微做了一点点的加工修饰
针对传入是字典的数据对象操作
<tr> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td>{{ student.get("gender") }}</td> </tr>
针对传入是列表套字典对象的操作
STUDENT_LIST = [ {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘}, {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘}, {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘} ]
<tr> <td>{{ foo }}</td> <td>{{ foo.name }}</td> <td>{{ foo.get("age") }}</td> <td>{{ foo["gender"] }}</td> </tr>
针对传入的是{1:{}}这种大字典类型
STUDENT_DICT = { 1: {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘}, 2: {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘}, 3: {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘}, }
<tr> <td>{{ foo }}</td> <td>{{ student.get(foo).name }}</td> <td>{{ student[foo].get("age") }}</td> <td>{{ student[foo]["gender"] }}</td> </tr>
后端传入多个数据类型,采用赋值语句传值方式,一个一个传!
@app.route("/allstudent") def all_student(): return render_template("all_student.html", student=STUDENT , student_list = STUDENT_LIST, student_dict= STUDENT_DICT)
Jinja2 的高阶用法
safe(前端入手)
将你写的html代码直接渲染不转译
Markup(后端入手)
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @app.route("/") def index(): tag = "<input type=‘text‘ name=‘user‘ value=‘DragonFire‘>" markup_tag = Markup(tag) # Markup帮助咱们在HTML的标签上做了一层封装,让Jinja2模板语言知道这是一个安全的HTML标签 print(markup_tag, type(markup_tag)) # <input type=‘text‘ name=‘user‘ value=‘DragonFire‘> <class ‘markupsafe.Markup‘> return render_template("index.html", tag=markup_tag) app.run("0.0.0.0", 5000, debug=True)
类似于Django里面的mark_safe()
模板中执行函数
先在后端中定义函数,类似于Django中的sample_tag/inclusion_tag等
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) #定义一个函数,把它传递给前端 def a_b_sum(a,b): return a+b @app.route("/") def index(): return render_template("index.html", tag=a_b_sum) app.run("0.0.0.0", 5000, debug=True)
通过tag参数将我们写的函数传递到前端页面
前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ tag }} <br> {{ tag(99,1) }} </body> </html>
看到结果就是,函数加()执行得到结果
还可以定义全局函数template_global(),无需后端传递给前端,Jinja2直接就可以执行的函数
对于这种处理函数,还可以有一种偏函数template_filter()的应用,管道符前面的值作为函数的第一个参数传入函数中
from flask import Flask from flask import render_template from flask import Markup # 导入 flask 中的 Markup 模块 app = Flask(__name__) @app.template_global() # 定义全局模板函数 def a_b_sum(a, b): return a + b @app.template_filter() # 定义全局模板函数 def a_b_c_sum(a, b, c): return a + b + c @app.route("/") def index(): return render_template("index.html", tag="") app.run("0.0.0.0", 5000, debug=True)
前端代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {{ a_b_sum(99,1) }} <br> {{ 1 | a_b_c_sum(197,2) }} </body> </html>
管道符前面也可以是一个函数,会将函数的返回值作为后面函数的第一个参数
Jinja2模板复用 block
跟Django里面的block模板语言一模一样,还有模板继承语言extends,模板应用语言include。跟Django里面的是一模一样的用法,这里不做赘述
Jinja2模板语言中的宏定义
简单的说就是在前端页面中直接定义函数,并直接在前端调用执行定义的函数
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Welcome OldboyEDU</h1> {% macro type_text(name,type) %} <input type="{{ type }}" name="{{ name }}" value="{{ name }}"> {% endmacro %} <h2>在下方是使用宏来生成input标签</h2> {{ type_text("one","text") }} {{ type_text("two","text") }} </body> </html>
宏定义一般情况下很少应用到,但是要知道有这么个概念,起码能吹个牛逼见识广博!
Flask实战
需求:
1. 用户名: oldboy 密码: oldboy123
2. 用户登录成功之后跳转到列表页面
3. 失败有消息提示,重新登录
4.点击学生名称之后,可以看到学生的详细信息
from flask import Flask from flask import request from flask import render_template from flask import redirect USER = {‘username‘: ‘oldboy‘, ‘password‘: "oldboy123"} STUDENT_DICT = { 1: {‘name‘: ‘Old‘, ‘age‘: 38, ‘gender‘: ‘中‘}, 2: {‘name‘: ‘Boy‘, ‘age‘: 73, ‘gender‘: ‘男‘}, 3: {‘name‘: ‘EDU‘, ‘age‘: 84, ‘gender‘: ‘女‘}, } app = Flask(__name__) @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") return render_template("login.html", msg=None) # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg @app.route("/student_list") def student(): return render_template("student_list.html", student=STUDENT_DICT) @app.route("/info") def student_info(): stu_id = int(request.args["id"]) stu_info = STUDENT_DICT[stu_id] return render_template("student.html", student=stu_info, stu_id=stu_id) app.run("0.0.0.0", 5000, debug=True) 相信写的代码才是最好的
后端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Welcome to Old Boy EDU</title> </head> <body> <form method="post"> 用户名:<input type="text" name="username"> 密码:<input type="text" name="password"> <input type="submit" value="登录"> {{ msg }} </form> </body> </html> 登录页面前端代码
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Old Boy EDU</title> </head> <body> Welcome to Old Boy EDU <table border="2xp"> <thead> <tr> <td>id</td> <td>name</td> <td>option</td> </tr> </thead> <tbody> {% for foo in student %} <tr> <td>{{ foo }}</td> <td>{{ student[foo].name }}</td> <td><a href="/info?id={{ foo }}">详细</a></td> </tr> {% endfor %} </tbody> </table> </body> </html> 一点儿也不难
student_list.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Old Boy EDU</title> </head> <body> Welcome to Old Boy EDU <table border="1px"> <thead> <tr> <td>id</td> <td>name</td> <td>age</td> <td>gender</td> </tr> </thead> <tbody> <tr> <td>{{ stu_id }}</td> <td>{{ student.name }}</td> <td>{{ student["age"] }}</td> <td>{{ student.get("gender") }}</td> </tr> </tbody> </table> <div><a href="/student_list">返回</a></div> </body> </html> 写不出来说明你没动脑子
student.html
Flask中的内置Session
Flask中的Session非常的奇怪,他会将你的SessionID存放在客户端的Cookie中,使用起来也非常的奇怪
1. Flask 中 session 是需要 secret_key 的(前提条件必须满足!)
from flask import session app = Flask(__name__) app.secret_key = "session_key"
secret_key 实际上是用来加密字符串的,如果在实例化的app中没有 secret_key 那么开启session一定会抛异常的
2. session 要这样用
@app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": if request.form["username"] == USER["username"] and request.form["password"] == USER["password"]: session["user"] = USER["username"] return redirect("/student_list") return render_template("login.html", msg="用户名密码错误") return render_template("login.html", msg=None) # 如果前端Jinja2模板中使用了msg,这里就算是传递None也要出现msg,防止报错!
session["user"] = USER["username"] 这样用就代表这个请求带上来的session中保存了一个user=name,后续就可以直接在session.get("user")中拿到设置的值
3. 怎么用 session 进行验证呢?
@app.route("/student_list") def student(): if session.get("user"): return render_template("student_list.html", student=STUDENT_DICT) return redirect("/login")
Flask中的路由系统
Flask中的路由系统其实我们并不陌生了,从一开始到现在都一直在应用
@app.route("/",methods=["GET","POST"])
1. @app.route() 装饰器中的参数
methods : 当前 url 地址,允许访问的请求方式(当前装饰的视图函数支持的请求方式)
@app.route("/info", methods=["GET", "POST"]) def student_info(): stu_id = int(request.args["id"]) return f"Hello Old boy {stu_id}" # Python3.6的新特性 f"{变量名}"
endpoint : 反向url地址,默认为视图函数名 (url_for)
from flask import url_for @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 Old boy {stu_id}" # Python3.6的新特性 f"{变量名}"
对比Django里面的url反向解析,这里相当于路由解析url
defaults : 视图函数的参数默认值{"nid":1}
from flask import url_for @app.route("/info", methods=["GET", "POST"], endpoint="r_info", defaults={"nid": 100}) def student_info(nid): print(url_for("r_info")) # /info # stu_id = int(request.args["id"]) print(nid) # 100 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}"
不常用
strict_slashes : url地址结尾符"/"的控制 False : 无论结尾 "/" 是否存在均可以访问 , True : 结尾必须不能是 "/",这一点不像Django会帮我们自动补全/
# 访问地址 : /info @app.route("/info", strict_slashes=True) def student_info(): return "Hello Old boy info" # 访问地址 : /infos or /infos/ @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos"
redirect_to : url地址重定向
需要注意的是这里的重定向并不是我们之前接触到那种先访问出一个页面,然后再跳转,而是直接在后端内部直接给你做重定向,可以称为内重定向。举个例子,相当于我有一个网站已经投入使用很久了,用户都将网站的地址收藏了,这个时候我需要扩展网站功能和修改部分功能,这些新功能对应的地址用户有又不知道,这个时候就可以在后端直接用上内重定向的方式,让用户访问的url在我们的服务器内部直接给他重定向到我们新的url地址中
# 访问地址 : /info 浏览器跳转至 /infos @app.route("/info", strict_slashes=True, redirect_to="/infos") def student_info(): return "Hello Old boy info" @app.route("/infos", strict_slashes=False) def student_infos(): return "Hello Old boy infos"
subdomain : 子域名前缀 subdomian="DragonFire" 这样写可以得到 DragonFire.oldboyedu.com 前提是app.config["SERVER_NAME"] = "oldboyedu.com"
app.config["SERVER_NAME"] = "oldboy.com" @app.route("/info",subdomain="DragonFire") def student_info(): return "Hello Old boy info" # 访问地址为: DragonFire.oldboy.com/info
对于初始化配置,我们需要知道的几种不同的配置方式:
app.config.from_object("setting.FlaskSetting")
app.DEBUG = True 开启Debug模式,该完代码不用手动重启
app.SECRET_KEY = "xxxxx" 开启session必备参数
app.config[‘DEBUG‘]=True
app.run(debug=True)
2.动态参数路由:
from flask import url_for # 访问地址 : http://127.0.0.1:5000/info/1 @app.route("/info/<int:nid>", methods=["GET", "POST"], endpoint="r_info") def student_info(nid): print(url_for("r_info",nid=2)) # /info/2 return f"Hello Old boy {nid}" # Python3.6的新特性 f"{变量名}"
<int:nid> 就是在url后定义一个参数接收,并且要是int类型,也可不加限制
但是这种动态参数路由,在url_for的时候,一定要将动态参数名+参数值添加进去,否则会抛出参数错误的异常
Flask实例化参数及配置
from flask import Flask app = Flask(__name__) # type:Flask app.config["DEBUG"] = True
这句 app.config["DEBUG"] = True 可以实现的功能可刺激了
代码只要发生改动,自动重启Flask程序(app.run)
在控制台打印的信息非常全面
以上两个功能就是传说中的 DEBUG 模式(调试模式)
Flask的配置就是在 app.config 中添加一个键值对,但是你存进去的键必须是config中应该存在的,如果不再存在的话,它会默认无用,就这么放着
config中有多少有用的key 呢?
{ ‘DEBUG‘: False, # 是否开启Debug模式 ‘TESTING‘: False, # 是否开启测试模式 ‘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 异常。 # 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。 # 如果这个值被设置为 True ,你只会得到常规的回溯。 ‘EXPLAIN_TEMPLATE_LOADING‘: False, ‘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, }
配置参数大全
修改配置的方式大约是两种
1.直接对app.config进行修改
app.config["DEBUG"] = True
2.使用类的方式导入
首先要有一个settings.py的文件
class FlaskSetting: DEBUG = True SECRET_KEY = "DragonFire"
然后我们在Flask的启动文件中就可以这么写
from flask import Flask app = Flask(__name__) # type:Flask app.config.from_object("settings.FlaskSetting")
它会自动对字符串路径进行处理并导入,相当于importlib
Flask实例化时候参数配置
static_folder = ‘static‘, # 静态文件目录的路径 默认当前项目中的static目录 static_host = None, # 远程静态文件所用的Host地址,默认为空 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 # host_matching是否开启host主机位匹配,是要与static_host一起使用,如果配置了static_host, 则必须赋值为True # 这里要说明一下,@app.route("/",host="localhost:5000") 就必须要这样写 # host="localhost:5000" 如果主机头不是 localhost:5000 则无法通过当前的路由 host_matching = False, # 如果不是特别需要的话,慎用,否则所有的route 都需要host=""的参数 subdomain_matching = False, # 理论上来说是用来限制SERVER_NAME子域名的,但是目前还没有感觉出来区别在哪里 template_folder = ‘templates‘ # template模板目录, 默认当前项目中的 templates 目录 instance_path = None, # 指向另一个Flask实例的路径 instance_relative_config = False # 是否加载另一个实例的配置 root_path = None # 主模块所在的目录的绝对路径,默认项目目录
需要我们记住的有:
static_folder = ‘static‘, # 静态文件目录的路径 默认当前项目中的static目录 static_url_path = None, # 静态文件目录的url路径 默认不写是与static_folder同名,远程静态文件时复用 template_folder = ‘templates‘ # template模板目录, 默认当前项目中的 templates 目录
这三个参数在后面的Flask蓝图会起到很大的作用
Flask中的蓝图(Blueprint)
Flask的蓝图,你可以看成是一个个小的组件,分别能够实现部分功能,在我的主运行文件中只需要将这些蓝图全部注册进来,就可以将这些分散的功能组合成一个整体,这非常符合实际开发需求,十个人可以同时开发不同的功能,最后直接统一注册整合即可拼成“真正的伟大蓝图”
1.初识Flask蓝图(blueprint)
Flask中的before_request,after_request
我们现在有一个 Flask 程序其中有3个路由和视图函数,如下:
from flask import Flask app = Flask(__name__) # type:Flask @app.route("/login") def login(): return "Login" @app.route("/index") def index(): return "Index" @app.route("/home") def home(): return "Login" app.run("0.0.0.0", 5000) 简单的小程序
小程序
如果登陆了,就可以访问 index 和 home 页面,如果没登录就跳转到 login 登录
要怎么解决呢, session 对, 用 session 除了 Login 函数之外的所有函数里面全校验 session 是否登录了
太麻烦了,现在咱们只有3个函数,如果成百上千个怎么整啊
装饰器,对没错,装饰器是一个很好的方案,但是啊,我现在还是成败上千个函数,我要在每一个函数定义的时候加上@装饰器,还是很麻烦
那么就引出了我们要学习的第一个知识点:
[email protected]_request 在请求(request)之前做出响应
from flask import Flask from flask import request from flask import redirect from flask import session app = Flask(__name__) # type:Flask app.secret_key = "DragonFire" @app.before_request def is_login(): if request.path == "/login": return None if not session.get("user"): return redirect("/login") @app.route("/login") def login(): return "Login" @app.route("/index") def index(): return "Index" @app.route("/home") def home(): return "Login" app.run("0.0.0.0", 5000)
解决所有问题
@app.before_request 也是一个装饰器,他所装饰的函数,都会在请求进入视图函数之前执行
request.path 是来读取当前的url地址如果是 /login 就允许直接通过 return None 你可以理解成通过放行
校验session中是否有user 如果没有的话,证明没有登录,所以毫不留情的 redirect("/login") 跳转登录页面
还有一个要提的 @app.before_first_request 它与 @app.before_request 极为相似或者说是一模一样,只不过它只会被执行一次
看到这里,学过Django的小伙伴们应该有一种似曾相似的感觉,就是这个方法特别像Django里面的中间件process_request方法~~~
2. @app.after_request 在响应(response)之前做出响应
@app.after_request def foot_log(environ): if request.path != "/login": print("有客人访问了",request.path) return environ
这个则类似于Django里面的process_response方法
[email protected](状态码)自动捕获状态码做出相应操作
函数接收的args参数就是原始的报错信息
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.
总结
如果我们在Flask中定义了还几个before_request和after_request装饰函数,如果在经历第一个before_request函数后就被拦截下来,那么如果是Django的中间件则会直接找到同一位置上的process_response去执行返回,而Flask中则是要走所有的after_request方法,无论你是在哪个before_request方法上被拦截的
原文地址:https://www.cnblogs.com/Dominic-Ji/p/9505608.html