Flask入门
一、Flask 简介
1 、简介
Flask诞生于 2010 年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量 级Web开发框架,又称之为微框架。微框架中的“微”意味着 Flask 旨在保持核心,Flask本身相当于一个 内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login),都需要用第 三方的扩展来实现。比如可以用 Flask-extension加入ORM、窗体验证工具,文件上传、身份验证等。 Flask没有默认使用的数据库,你可以选择MySQL,也可以用NoSQL。其 WSGI 工具箱采用 Werkzeug(路由模块) ,模板引擎则使用 Jinja2 。 Django框架是尽可能多的提供功能给你,就象一 个暖男一样,只要你想要我就提供给你。而Flask不同,Flask设计的思想是每个人开发的web可能都不 一样,用到的技术点也都是不一样的。如果都帮web开发者设计好的话可能会束缚开发者的思想,所以 Flask只提供最核心的路由分发,你要什么功能自己集成进来,Flask给你提供最大的自由性,所以这儿也 是Flask受欢迎的原因之一。
Flask也算是python众多web框架中最灵活的框架。
Flask中文版文档: http://www.pythondoc.com/flask/
常用第三方扩展包:
Flask-SQLalchemy:操作数据库;
Flask-migrate:管理迁移数据库;
Flask-Mail:邮件;
Flask-WTF:表单;
Flask-script:插入脚本;
Flask-Login:认证用户状态;
Flask-RESTful:开发REST API的工具;
Flask-Bootstrap:集成前端Twitter Bootstrap框架;
Flask-Moment:本地化日期和时间;
Flask-Uploads: 上传文件处理
2 、 Flask 与 Django 的对比
Django
Django功能齐全,它提供一站式解决方案,集成了MVT(Model-View-Template)和ORM,以及后台 管理。用一个很形象的例子: Django就像一个精装修的房子,什么家具(功能)都有,直接拎包入住。
django提供了:
django-admin 快速创建项目工程目录 manage.py 管理项目工程 orm 模型(数据库抽象层) admin 后台管理站点 缓存机制 文件存储系统 用户认证系统
Flask
Flask相对于Django而言是轻量级的Web框架,Flask的两个主要核心应用是Werkzeug和模板引擎 Jinja2。 django的后台管理,表单,orm等都有现成的直接使用,而是Flask都没提供,需要使用第三方扩展 包。
Flask就好比待装修的房子:
二、创建项目
1 、 创建 Flask 工程
创建Flask项目没有命令快捷方式,只需要在项目文件夹下创建一个普通的py文件,导入Flask实例化一 个Flask应用程序。
示例: 提示: 通过pycharm的运行命令 运行flask项目 通过python app.py 命令行方式运行flask项目
2 、 初始化参数
import_name: 导包的目录
static_url_path: 访问静态文件的url前缀
static_folder: 默认‘static’
template_folder: 默认‘templates’实例化Flask对象之后,静态文件默认在Flask第一个参数指定的 模块所在的目录下,静态文件使用
目录,模板使用 目录。
# 导入Flask类
from flask import Flask
# Flask 接收一个参数 name ,当前模块的文件名
# Flask 在查找静态文件,或者模板时候默认以当前文件所在的目录去查找
# 如果传一个不存在的模块名,将默认使用当前文件
app = Flask(__name__)
# 装饰器将路由映射到视图index
@app.route('/')
def index():
return "ok"
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
app.run(debug=True)
在static目录下放一张图片 01.jpg,然后通过静态文件路径可以访问到:http://127.0.0.1:5000/static/ 01.jpg
3 、 配置
django项目中有一个settings.py的配置文件,但是Flask中没有,需要自己定义。
常见的两种方式:
-
从配置文件中读取配置
app.config.from_pyfile(‘文件名’)
-
从类中读取配置信息
app.config.from_object(类名)
Flask 默认是运行在线上模式,也就是说出错了是看不到错误堆栈信息的,为了好调试一般开发阶 段会将调试打开。django中默认开启调试DEBUG = True,Flask中开启调试同样只需要在配置中写 入 DEBUG=True。 从配置文件中读取配置 示例: 创建一个文件 保存配置信息
注意:配置的变量名需要用大写。如果注册多个配置文件,后面注册的同名配置会覆盖掉前面的配 置。一般使用一个工程只用一个配置文件。
#导入Flask类 from flask import Flask
# Flask 接收一个参数 name ,
# 导入模块的目录, flask以这个目录为基础,寻找静态文件目录static和模板目录templates
app = Flask(__name__,
# static_url_path='/python',
# 访问静态资源的url前缀,默认是/static
static_folder='static',
# 静态文件目录名,默认static
template_folder='templates', # 模板文件目录名,默认templates
)
# 装饰器将路由映射到视图index
@app.route('/')
# 定义一个视图
def index():
return 'hello'
# 设置配置信息获取方式,从配置文件中查找
# app.config.from_pyfile('config.cfg')
class Config(object):
DEBUG = False
# 设置配置信息获取方式,从配置对象中查找
app.config.from_object(Config)
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
app.run()
4 、读取配置参数
第一种方式:
在能访问到app对象的文件中可直接使用app.config.get方式获取配置参数 app.config.get(‘配置’)
第二种方式:
需要从flask中导入current_app,在整个Flask项目中都可以使用,实际上也是app对象 相到于一个别名。 current_app.config.get(‘配置名’)
# 导入Flask类
from flask import Flask,current_app
# Flask 接收一个参数 name ,
# 导入模块的目录, flask以这个目录为基础,寻找静态文件目录static和模板目录templates
app = Flask(__name__,
# static_url_path='/python',
# 访问静态资源的url前缀,默认是/static
static_folder='static', # 静态文件目录名,默认static
template_folder='templates', # 模板文件目录名,默认templates
)
# 装饰器将路由映射到视图index
@app.route('/')
# 定义一个视图
def index():
#a = current_app.config.get('A')
a = app.config.get('A')
b = app.config.get('B')
return str(a + b)
# 设置配置信息获取方式,从配置文件中查找
# app.config.from_pyfile('config.cfg')
class Config(object):
DEBUG = False
A = 2
B = 5
# 设置配置信息获取方式,从配置对象中查找
app.config.from_object(Config)
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
app.run()
5 、app.run参数
run方法源码
from werkzeug.serving import run_simple
def run( self,
host: t.Optional[str] = None,
port: t.Optional[int] = None,
debug: t.Optional[bool] = None,
load_dotenv: bool = True,
**options: t.Any,
) -> None:
if debug is not None:
self.debug = bool(debug)
server_name = self.config.get("SERVER_NAME")
sn_host = sn_port = None
if server_name:
sn_host, _, sn_port = server_name.partition(":")
if not host:
if sn_host:
host = sn_host
else:
host = "127.0.0.1"
if port or port == 0:
port = int(port)
elif sn_port:
port = int(sn_port)
else:
port = 5000
from werkzeug.serving import run_simple
try:
run_simple(t.cast(str, host), port, self, **options)
finally:
self._got_first_request = False
从源码中可以看出run方法是启动一个werkzug工具箱提供的简易服务器。
参数:
host 服务器主机地址,默认使用本机地址’127.0.0.1’ ,假如设置’0.0.0.0’,则可以监听本机所有ip地址 port 端口号,默认 5000
debug 调试,参数是bool值 ,表示是否开启调试,True开启调试。默认Flase
三、路由配置
路由就是根据请求的 url 找到对应处理的函数视图的过程。在请求之前应该建立好一张路由表保存url 与视图的对应关系,这样有请求过来才能正确找到对应的视图。
Flask中有两种常用方式构建路由规则:
1 、@app.route(‘url规则’) decorator装饰器方式
2 、app.add_url_rule()
add_url_rule(self, rule, endpoint=None, view_func=None, **options)参数的含义如下:
rule: url 规则字符串,可以是静态的 /path,也可以包含 / endpoint:要注册规则的 endpoint,默认是 view_func 的名字 view_func:对应 url 的处理函数,也被称为视图函数
# 装饰器将路由映射到视图index
@app.route('/')
# 定义一个视图
def index():
return 'ok'
#这两种方法是等价的
#app.add_url_rule('/', 'index', index)
1 、查看路由信息
在django中url统一配置在URLconf配置文件中,但是Flask直接配置在视图没有统一的配置文件如何查 看路由信息呢?
app.url_map查看所有路由
示例:
#导入Flask类
from flask import Flask
app = Flask(__name__)
# 定义一个视图
# @app.route('/')
def index():
print(app.url_map)
return 'hello'
# @app.route('/detail')
def detail():
return 'detial'
# @app.route('/list')
def list():
return 'list'
app.add_url_rule('/', endpoint='index', view_func=index)
app.add_url_rule('/detail', endpoint='detail', view_func=detail) app.add_url_rule('/list', endpoint='list', view_func=list)
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
app.run(debug=True)
执行视图会打印出下面url信息:
每一条 Rule对应信息:
- url规则
- 支持的请求方式,默认支持get请求,不支持post.
- 对应的视图
Map(([<Rule '/' (OPTIONS, HEAD, GET) -> index>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
可以在python shell中导入flask项目查看:
In [5]: from app import app
In [6]: app.url_map
Out[6]:
Map(([<Rule '/' (OPTIONS, HEAD, GET) -> index>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
2 、同一路由装饰不同视图
同一个路由规则装饰不同函数,只有第一个视图函数会匹配到。虽然都生成了路由表,但是url匹配中第 一个规则之后就会调用视图,不再继续往下匹配。
# 同一个url规则装饰不同的视图函数
@app.route('/index')
def index1():
return 'index1'
@app.route('/index')
def index2():
return 'index2'
3 、一个视图函数多个路由装饰器
同一个视图函数有多个路由装饰器,会生成多条路由信息,每条对应规则的url都可以访问到视图函 数。
4 、methods 参数
HTTP (与 Web 应用会话的协议)有许多不同的访问 URL 方法。默认情况下,路由只回应 GET 请 求,但是通过route() 装饰器传递 methods 参数可以改变这个行为
methods 参数接收一个字典,元素为字符串形式的请求方式名称。 如果不传methods参数,默认支 持 GET, HEAD,OPTIONS
OPTIONS 给客户端提供一个敏捷的途径来弄清这个 URL 支持哪些 HTTP 方法。 从 Flask 0.6 开始,实 现了自动处理。
HEAD 浏览器告诉服务器:欲获取信息,但是只关心 消息头 。应用应像处理 GET 请求一样来处理 它,但是不分发实际内容。在 Flask 中你完全无需 人工 干预,底层的 Werkzeug 库已经替你打点好 了。
定义一个视图
@app.route('/login', methods=['GET', 'POST'])
def login():
print(app.url_map)
if request.method == 'GET':
return 'get'
elif request.method == 'POST':
return 'post'
5 、反向解析
url_for() 函数 可以通过视图函数名称,反向解析得到视图对应的url
url_for函数:
from flask import Flask, url_for
app = Flask(__name__)
@app.route("/index/python")
def req_python():
return 'python'
@app.route("/url_for")
def req_url():
"""url_for 反向解析"""
return '[链接](%s)' % url_for("req_python")
if __name__ == '__main__':
app.run(debug=True)
6 、动态路由
要给 URL 添加变量部分,把这些特殊的字段标记为 , 这个部分将会作为命名参数 传递到视图函数中。
变量放在<> 中
from flask import Flask
app = Flask(__name__)
@app.route('/param/<name>')
def get_url_param(name):
return '参数是:%s' % name
if __name__ == '__main__':
app.run(debug=True)
规则可以用 converter:variable_name 指定一个可选的转换器。
转换器: 默认匹配的是不带/的字符串
int: 接受整数
float:接受浮点数
path: 和默认的相似,但也接受斜线
# 默认<>的规则匹配不带/的整数
@app.route('/param_int /<int:id>')
def get_url_param_int(id):
return '获取的参数是: %s '% id
# 默认<>的规则匹配不带/的 浮点数
@app.route('/param_float/<float:f>')
def get_url_param_float(f):
return '获取的参数是: %s '% f
# 匹配参数后面带/
@app.route('/param_path/<path:p>')
def get_url_param_path(p):
return '获取的参数是: %s '% p
7 、自定义正则转换器
Flask路由转换器,没有提供基于正则的,但是我们可以自定义基于正则的路由转换器。
-
自定义转换器必须继承BaseConverter类,自定义转换器需要重写父类的__init__方法
-
注册转换器,url_map中保存了所有的路由转换器,是字典类型
from werkzeug.routing import BaseConverter
from flask import Flask, url_for
app = Flask(__name__)
# 正则转换器
class RegexConverter(BaseConverter):
def __init__(self, url_map, *args):
# 调用父类的初始化方法
super(RegexConverter, self).__init__(url_map)
# 将正则表达式传给转换器对象,flask在解析路径的时候,会来这里获取regex保存的正则表达
式
self.regex = args[0]
def to_python(self, value):
# 对获取到的参数进行处理
# 默认获取到是字符串,可以对获取到的参数进行处理,比如类型转换
# 这样就可以在视图中直接使用
print(type(value))
print(value)
return value
def to_url(self, value):
# 当使用反解析的时候会调用这个方法,可以对参数进行处理
print(value)
return value
# 注册re 转换器 RegexConverter
app.url_map.converters['re'] = RegexConverter
@app.route("/param_re/<re('\d'):num>/<re('\d+'):num2>")
def get_param_re(num, num2):
"""url中提取参数"""
return '自定义正则转换器获取参数1:%s , 参数2:%s' % (num, num2)
@app.route('/get_param_re_url/')
def get_param_url():
"""在视图函数中获取url"""
return '<a href="%s">to_url演示</a>' % (url_for('get_param_re', num='1',num2='2'))
四、处理请求
1 、 request 对象
Flask中获取请求数据,与django中不同,django视图中第一个参数必须是HttpRequest对象,Flask 中需要导入从flask中导入request对象,request对象中已经封装当前所有的请求参数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IUpZaemS-1634869750942)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211019171757374.png)]
与路径相关的属性
path、script_root、url、base_url、url_root
比如用户访问这个链接: http://www.example.com/myapplication/page.html?x=y
这个情况下,上面提到的属性的值会为如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHuUDunr-1634869750946)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211019171851332.png)]
from flask import Flask, request
app = Flask(__name__)
@app.route('/python/index')
def index():
print(request.path)
print(request.script_root)
print(request.base_url)
print(request.url)
print(request.url_root)
return '请求参数测试'
if __name__ == '__main__':
app.run(debug=True)
2 、 Postman 插件使用
Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件。无论是web前端开发 或 android、ios开发,只要涉及调用后端接口,postman这类型工具就必不可少了 利用这个插件模拟浏览器以不同的请求方式向服务器提交参数
2.1 下载及加载Postman
- 使用chrome商店在线下载postman
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7TXpHdq3-1634869750949)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211019172101683.png)]
离线下载postman插件,可以自己到网上下载
-
解压文件,在解压后的文件夹中找到.crx文件,将.crx改为.zip或.rar,并且解压
-
把_metadata 文件夹 改为metadata
-
文件夹 加载Postman
-
启动Postman
进入 chrome://apps/
2.2 使用Postman
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sLcAwugE-1634869750953)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211019172427993.png)]
from flask import Flask, request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def index():
# form 获取post提交的数据
post_dict = request.form
print("表单数据: ", post_dict)
# args 获取查询字符串,即url ? 后面的参数
query_dict = request.args
print("查询字符串: ", query_dict)
# 获取所有查询参数,跟post参数
all_dict = request.values
print("所有参数: ", all_dict)
# 获取cookie
cookie = request.cookies
print(cookie)
# 获取某个参数值get方法,如果不存在这个key会报错,为了不报错可以传一个默认值,如果不存在
key,将使用默认值。
# 如果是多值使用getlist方法,如果不存在,则返回空列表。
a = query_dict.get('a', '')
b = query_dict.getlist('b')
print("参数a: ", a)
print("参数b: ", b)
return 'ok'
if __name__ == '__main__':
app.run(debug=True
3 、文件上传
已上传的文件存储在内存或是文件系统中一个临时的位置。你可以通过请求对象的 files 属性访问它 们。每个上传的文件都会存储在这个字典里。它表现近乎为一个标准的 Python file 对象,但它还有一 个 save() 方法,这个方法允许你把文件保存到服务器的文件系统上。
前端Postman:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rp36Brvf-1634869750955)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211019172622582.png)]
后端Flask:
from flask import Flask, request
app = Flask(__name__)
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 查看文件上传列表
# print(request.files)
f = request.files['myfile']
f.save('uploaded_file.txt')
return "文件上传成功..."
else:
return "您的请求方式不正确..."
if __name__ == '__main__':
app.run(debug=True)
如果你想知道上传前文件在客户端的文件名是什么,你可以访问 filename 属性。。如果你要把文件按 客户端提供的文件名存储在服务器上,那么请把它传递给 Werkzeug提供的 secure_filename() 函数: 传递一个文件名,它会返回一个安全的文件名,防止上传的文件名不符合服务器文件命名要求。
示例:
from flask import request
from werkzeug.utils import secure_filename
@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
# 判断是Post请求方式
if request.method == 'POST':
f = request.files['myfile']
f.save('./uploads/' + secure_filename(f.filename))
return "文件上传成功..."
else:
return "您的请求方式不正确..."
注意:secure_filename 不能识别中文。 secure_filename仅返回ASCII字符。所以, 非ASCII(比如汉 字)会被过滤掉,空格会被替换为下划线。你也可以自己处理文件名自动生成一个随机文件名,或是在 使用这个函数前将中文替换为拼音或是英文。2435357ab563475afdc.jpg
使用md5值作为文件名****
from flask import Flask, request
from werkzeug.utils import secure_filename
from hashlib import md5
app = Flask(__name__)
@app.route('/uploads', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
# 查看文件上传列表
# print("上传的文件列表: ",request.files)
f = request.files['myfile']
# 查看上传的文件名
print("上传的文件名: ", f.filename)
# 1.直接使用上传的文件名
# f.save('./uploads/' + f.filename)
# 2.使用secure_filename()处理过的文件名
# f.save('./uploads/' + secure_filename(f.filename))
# 3.使用md5值作为文件名 a图片.png--->bytes(编码成二进制串)
md5_filename = md5(f.filename.encode('utf-8')).hexdigest()
# 从右侧开始查找圆点的索引值
dot_index = f.filename.rindex('.')
# 获取上传文件的后缀.png
file_suffix = f.filename[dot_index:]
new_file_name = md5_filename + file_suffix
print("新的文件名:", new_file_name)
f.save(f'./uploads/{new_file_name}')
return "文件上传成功..."
else:
return "您的请求方式不正确..."
if __name__ == '__main__':
app.run(debug=True)
五、 处理响应
1、响应数据
1) 关于响应
视图函数的 return 值会自动转换为一个响应对象。如果返回值是一个字符串, 它被转换为该字符串为 主体的、状态码为 200 的 ,MIME 类型是 text/html 的响应对象。 Flask 把返回值转换为响应对象的逻辑:
如果返回的是一个字符串,响应对象会用字符串数据和默认参数创建。
如果返回的是一个元组,且元组中的元素可以提供额外的信息。 这样的元组必须是 (response, status, headers) 形式且至少含有一个元素。 status 值将会覆盖状态代码,headers 可以是一 个列表或额外的消息头值字典 。
如果返回的是一个合法的响应对象,它会从视图直接返回。
我们之前都是直接返回字符串,现在测试一下 返回元组:
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
# 1.直接返回字符串,则flask会默认构建一个响应对象返回
# return "人生苦短,我用python"
"""
2.返回元组 :(response,status,headers)
参数介绍: 一个参数是响应体,第二个参数是响应状态码,第三个参数响应头 可以是一个字典
flask会按照参数的设置返回相应的响应对象
"""
return ("人生苦短,我用p", '206 very good', {'Server': 'zzt_Server1.0','subject': 'Flask'})
if __name__ == '__main__':
app.run(debug=True)
2) 响应对象
使用make_response 创建响应对象
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/index')
def index():
# 使用make_response()函数构建Response响应对象
# response = make_response('Flask框架讲解', '404 File Not Found', {'subject':'python'})
response = make_response()
# 自定义响应体
response.data = "flask框架"
# # 自定义状态码
response.status = '206 ok'
# # 自定义请求头
response.headers['subject'] = 'Flask'
# 返回响应对象
return response
if __name__ == '__main__':
app.run(debug=True)
3) 返回 模板页面
使用 render_template 方法渲染模板并返回
前端模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
我的模板html内容
<br/>{{ 变量1 }}
<br/>{{ 变量2 }}
</body>
</html>
后端视图
from flask import Flask, render_template, make_response
import re
app = Flask(__name__)
@app.route('/index')
def index():
product = "iphone13"
price = 5999
# 【方式一】
# # 使用make_response()函数构建Response响应对象
# response = make_response()
# # 自定义响应体
# data = open('./templates/demo.html', 'r', encoding='utf-8').read()
# data = re.sub('{{ product }}', product, data)
# data = re.sub('{{ price }}', str(price), data)
# response.data = data
# # 返回响应对象
# return response
# 【方式二】 使用render_template函数
return render_template('demo.html', product=product, price=price)
if __name__ == '__main__':
app.run(debug=True)
在前面内容中我们都是返回字符串,但是很多web开发中都是要求前后端分离的,前端一般会要求后 端返回json数据。
在django的学习中我们知道返回json数据有一个JsonResponse对象,接收一个python字典作为参 数。Flask中也有一个类似的对象可以返回json数据。 jsonify: 返回json格式的数据
from flask import Flask, make_response, jsonify
import json
app = Flask(__name__)
@app.route('/index')
def request_json():
data = {
'a': 1,
'b': 2
}
# 普通方式返回json数据
# 将字典对象转为json字符串
# resp_json = json.dumps(data)
# 创建一个响应对象
# response = make_response(resp_json)
# 添加响应头,表示返回的是json数据
# response.headers["Content-Type"] = "application/json"
# return response
# 将字典转换成json对象后返回
return jsonify(data)
if __name__ == '__main__':
app.run(debug=True)
2、异常处理
1) abort 函数
abort函数的作用是:放弃请求并返回错误代码
相当于python中的raise 抛出异常
详细HTTP状态码 :https://tool.oschina.net/commons?type=5
# 导入Flask类
from flask import Flask, abort
app = Flask(__name__)
@app.route('/')
def index():
# 终止视图执行,并返回HTTP状态码
abort(500)
return 'ok'
if __name__ == '__main__':
app.run(debug=True)
2 )自定义错误处理视图
使用 errorhandler 装饰器,接受一个http状态码为参数。
自定义的错误视图不单单作用于abort函数抛出的错误,也作用于整个Flask应用对应错误码。 自定义错误处理视图接收一个参数,是Flask应用的默认报错信息
# 导入Flask类
from flask import Flask, abort
app = Flask(__name__)
@app.route('/')
def index():
# 终止视图执行,并返回HTTP状态码
abort(500)
return 'ok'
# 自定义错误处理视图函数
# 使用 errorhandler 装饰器,接受一个http状态码为参数。
# 自定义的错误视图不单单作用于abort函数抛出的错误,也作用于整个Flask应用对应错误码。
# 自定义错误处理视图接收一个参数,是Flask应用的默认报错信息
@app.errorhandler(403)
def error(e):
return f'禁止访问,可能权限不够,具体错误信息: <br/>{e}'
if name == '__main__':
app.run(debug=True)
3、重定向
redirect 函数
Flask中的重定向使用 redirect 函数,接收一个url为参数
案例:
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/index')
def index():
"""index视图"""
return '我是首页'
@app.route('/login')
def login():
# 重定向到 index 视图,使用url_for反解析获得url
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
六、Cookie与Session
1、Cookie
1) Cookie 简介
response对象的 set_cookie 方法来设置 Cookies。 request对象的 cookies 属性是客户端提交过来的 所有cookie键值对,字典类型。
2) 设置 cookie
cookie 是以键值对的形式保存在浏览器中。
设置cookie我们比较关心的三个参数:
key cookie的键
value cookie的值
max_age=None 超时时间,单位是秒
expires=None 超时时间,datatime对象
import datetime
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/set_cookie')
def set_cookie():
"""设置cookie"""
resp = make_response('设置cookie')
# 向浏览其中写入一个key为a,值为python,超时时间在2021,10,19日
# resp.set_cookie('a', 'python', expires=datetime.datetime(year=2021,month=10, day=19))
# 向浏览其中写入一个key为a,值为python,超时时间是60*60秒之后
resp.set_cookie('a', 'python', max_age=60*60)
return resp
if __name__ == '__main__':
app.run(debug=True)
3) 获取 cookie
request 对象中的cookie属性是包含了所有浏览器上传到服务器的cookie。 get 方法获取其中的键 值,可以传一个默认值。
@app.route('/get_cookie')
def get_cookie():
"""获取cookie"""
# 通过键获取cookie值,如果没有这个键返回None
a = request.cookies.get('a')
print(a)
# 可以传一个默认值,如果不存在这个键,将使用默认值。
b = request.cookies.get('b', 'not found')
print(b)
return f'获取到的cookie是: a:{a} b:{b}'
4) 、 删除 cookie
@app.route('/del_cookie')
def del_cookie():
"""删除cookie"""
response = make_response('删除cookie')
# 将key为a的cookie 删除
response.delete_cookie('a')
return response
2、Session
1) Session简介
在学习django中的所有的session是保存在服务器中的数据库的。将sessionid写到浏览器,浏览器发 送请求的时候 将sessionid传回给服务器,然后再利用sessionid到数据库中匹配对应用户的session信 息。django中创建好项目就有默认的数据库,这个保存在数据库中好理解。
但是在Flask中,我们并没有数据库配置,那么session信息保存在哪个位置呢。实际上Flask的session 是基于cookie加secret_key 进行加密后保存在cookie中的。
session具体的使用方法:
首先设置SECRET_KEY:
# Flask中需要使用session必须先配置SECRET_KEY 或者 自己输入一个字符串
app.config['SECRET_KEY'] = os.urandom(24)
app.config['SECRET_KEY'] = 'sdfsdfs&&^%dsdf*/*$#'
----------
import os
os.urandom(n)
返回n个字节的适合加密的随机字节串
视图函数中使用session跟python字典类似使用key获取值,或者使用get方法。
案例
import os
from flask import Flask, session
app = Flask(__name__)
# app.config['SECRET_KEY'] = 'sdfsdfs&&^%dsdf*/*$#'
app.config['SECRET_KEY'] = os.urandom(24)
@app.route('/set_session')
def set_session():
# 设置session 字典形式
session['name'] = 'python'
session['password'] = '123456'
return 'session'
@app.route('/get_session')
def get_session():
# 获取session 采用[key]方式取值,如果key不存在会报错。
name = session['name']
print(name)
# 获取session 采用get方式取值,如果key不存在返回None,
pwd = session.get('password')
print(pwd)
return 'name:%s ' % name
if __name__ == '__main__':
app.run(debug=True)
2 ) 删除session
可以直接使用session.pop(‘key’,None):
session.pop(‘name’,None)
如果要删除session中所有数据使用:
clear(): session.clear()
@app.route('/del_session')
def del_session():
# 删除session的某个键值对,返回删除的session值on,如果不存在可以设置一个默认值。
result = session.pop('name', None)
# 清除整个session.
session.clear()
return '删除session %s ' % result
3) 设置session超时时间
Flask的默认session利用了Werkzeug的SecureCookie,把信息做序列化(pickle)后编码(base64),放 到cookie里了。
过期时间是通过cookie的过期时间实现的。
过期时间是这样来设置:
from datetime import timedelta
from os import urandom
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
app.secret_key = urandom(24)
@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():
# 设置session的持久性为True
session.permanent = True
# 设置session的有效时间 :两分钟后失效
app.permanent_session_lifetime = timedelta(minutes=2)
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form 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'))
if __name__ == '__main__':
app.run(debug=True)
七、请求钩子与上下文
1、请求钩子
在每个请求执行视图之前或者执行完成之后,需要做一些操作,为了避免每个视图都重写重复代码, Flask提供了四个通用函数完成一系列操作,即请求钩子。
请求钩子使用修饰器实现。Flask常用的钩子函数:
before_first_request:注册一个在处理第一个请求之前运行的函数。 before_request:注册一个在处理请求之前运行的函数。
after_request:注册一个函数,如果没有未处理的异常抛出,在每次请求之后运行,接收一个响应对 象为参数,需要返回一个响应对象
teardown_request:注册一个函数,即使有未处理的异常抛出,也在每次请求之后运行,接收一个响 应对象为参数,需要返回一个响应对象。
import time
from flask import Flask, Response
app = Flask(__name__)
# 只有在第一次请求的时候执行
@app.before_first_request
def first_request():
print("before_first_request 执行")
# 每次请求之前执行
@app.before_request
def before_request_():
print('before_request执行') # 每次执行完成后没有未处理的异常抛出才会执行
# 每次请求执行完后执行,需要传一个Response参数,且返回一个修改后的Response对象
@app.after_request
def after_request(response: Response):
print('after_request 执行')
# 添加响应头
response.headers["Content-Type"] = "application/json"
return response
# 每次请求完最后执行,需要传一个异常参数
@app.teardown_request
def teardown_request_(e):
print('teardown_request 执行...')
print("teardown_request", e)
return e
@app.route('/contacts')
def query_contacts():
print('查询本单位联系人')
# a = 1 / 0
# raise Exception("文件异常...")
return '本单位人员通讯录...'
@app.route('/login')
def login():
return "登录页面..."
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
app.run() # 出现异常时,只有debug=False 才会调用@app.teardown_request函数
2、Flask 上下文
1) 应用上下文
Flask 中有两个应用上下文对象:
current_app 对象
g 对象
2) current_app 对象
current_app 对象,它被绑定到当前请求的应用的引用。
如果在程序中需要访问应用,那么需要将应用显式地到处传递应用,如果使用current_app对象,我们 不需要关心创建的应用,当我们访问current_app对象的时候,实际上是访问请求的应用。
# 导入Flask类
from flask import Flask, current_app
# 在创建app应用的时候,每个创建的对象名可能都不一样。
# 使用current_app 应用上下文,我们可以不用关心应用怎么创建的。
# app = Flask( name )
# app_2 = Flask( name )
my_app = Flask(__name__)
class Config(object):
USERNAME = 'hongfei'
PASSWORD = '123456'
my_app.config.from_object(Config)
@my_app.route('/')
def index():
username = my_app.config.get("USERNAME") # 可以不通过Flask实例名称获取配置
password = current_app.config.get("PASSWORD") # 获取配置
return f'username={username},password={password}'
if __name__ == '__main__':
# Flask 应用程序实例的方法run启动web服务器
my_app.run(debug=True)
3) g 对象
g对象是应用上下文的一种,每一个请求过来都会创建一个g对象。g对象就是一个作用于app应用的全 局变量。每个请求进来g对象都先置为空。
在钩子函数与视图函数中变量的传递,可以用g对象做为全局变量去传递。
from flask import Flask, g
# 每次请求之前执行
app = Flask(__name__)
@app.before_request
def before_request_():
g.s = 'g对象传过来的参数'
print('before_request执行')
@app.route('/')
def index():
print('index 执行')
# 在视图函数中通过g对象获取参数
print(g.get('s'))
return 'ok'
@app.teardown_request
def teardown_r(e):
print("teardown_r", g.get('s'))
return e
if __name__ == '__main__':
app.run()
4) 请求上下文
请求上下文保存了客户端和服务器交互的数据
Flask 中请求上下文有两个对象:
request 对象
关心应用怎么创建的。
# app = Flask( name )
# app_2 = Flask( name )
my_app = Flask(name)
class Config(object):
USERNAME = ‘hongfei’
PASSWORD = ‘123456’
my_app.config.from_object(Config)
@my_app.route(’/’)
def index():
username = my_app.config.get(“USERNAME”) # 可以不通过Flask实例名称获取配置
password = current_app.config.get(“PASSWORD”) # 获取配置
return f’username={username},password={password}’
if name == ‘main’:
# Flask 应用程序实例的方法run启动web服务器
my_app.run(debug=True)
##### 3) g 对象
g对象是应用上下文的一种,每一个请求过来都会创建一个g对象。g对象就是一个作用于app应用的全 局变量。每个请求进来g对象都先置为空。
在钩子函数与视图函数中变量的传递,可以用g对象做为全局变量去传递。
```python
from flask import Flask, g
# 每次请求之前执行
app = Flask(__name__)
@app.before_request
def before_request_():
g.s = 'g对象传过来的参数'
print('before_request执行')
@app.route('/')
def index():
print('index 执行')
# 在视图函数中通过g对象获取参数
print(g.get('s'))
return 'ok'
@app.teardown_request
def teardown_r(e):
print("teardown_r", g.get('s'))
return e
if __name__ == '__main__':
app.run()
4) 请求上下文
请求上下文保存了客户端和服务器交互的数据
Flask 中请求上下文有两个对象:
request 对象
session 对象