Flask
Flask第三方组件非常全,适合小型 API服务类项目,但第三方组件运行稳定性相对Django差。
基础知识
Flask安装
pip install flask==2.0.3
Flask库文件
- Jinjia2:模板渲染库
- Markupsafe:返回安全标签 只要Flask返回模板或者标签时都会依赖
- Werkzeug:德文
工具
底层是基于 WSGI,Flask项目启动都是基于Werkzeug - itsDangerous: 安全地对数据进行签名,以确保其完整性。用于保护Flask的会话Cookie
- Click用于编写命令行应用程序的框架,提供命令并允许添加自定义管理命令
启动Flask
# 导入Flask类创建Flask应用对象
from flask import Flask
app = Flask(__name__)
# 为Flask应用添加路由
@app.route("/")
def index(): # 与路由绑定的视图函数,视图函数名尽可能保持唯一
return "Hello world"
if __name__ == "__main__":
app.run() # 启动Flask应用
Flask Response
-
“Hello world” 可直接返回字符串
-
render_template(“HTML文件”)
-
redirect(“/home”) 重定向到指定路由,ResponseHeaders中加入了一个 Location:http://url
-
send_file(“文件路径”) 返回文件
打开并识别文件内容,自动识别文件类型,在ResponseHeaders中加入Content-Type:文件类型,这些文件类型是可以被客户端识别的,对于不能识别的文件类型,作下载处理。
如exe文件:x-ms x表示二进制文件 ms表示微软
-
jsonify(“字符串或者数据类型”)
返回数据类型为Content-Type:application/json
Flask Request
-
request.form:获取FormData中的数据,可用to_dict()方法转为字典数据类型
-
request.method:获取请求方式
-
request.args:获取url中的数据
-
request.files:获取上传的文件
my_file = request.files.get("myfile") my_file.save("文件保存路径")
-
request.json:获取json数据
获取Content-Type:application/json时提交的数据
-
request.data:获取原始请求体中的数据 b""
Content-Type无法被识别或不包含Form字眼
-
request.headers
-
request.cookies
-
request.path
-
request.host
-
request.host_url
-
request.url:请求地址
-
request.values:接受所有(GET,POST)请求中的数据,包含了URL和FormData
-
request.url_charset:URL编码格式
-
request.url_root:请求地址,完整的请求地址 host
-
request.url_rule:请求路由地址 /index
用户登录demo
Python
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
return render_template("login.html")
elif request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
print(username, password)
if username == "jack" and password == "123456":
return {"msg": "登陆成功"}
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
Html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户登录</title>
</head>
<body>
<body>
<form action="/login" method="post">
<span>账号 <input type="text" name="username"></span><br>
<span>密码 <input type="text" name="password"></span><br>
<span><input type="submit" value="登录"></span>
</form>
</body>
</body>
</html>
Flask Session
Session:服务端的键值对
Cookie:客户端的键值对
交由客户端保管机制:
- 开启session[token] = “asdfihasdfjhdsfiohoi” => {”token": “asdfihasdfjhdsfiohoi”}
- 序列化字典 => 字符串
- 根据
secret_key
密钥加密字符串
接受反序列化session:
- 从cookie中获取key为session的值 “eyJ0b2tlbiI6ImFzZGloaWFzZGhhc2RpdWhhaXNkaCJ9.Y3Oe1w.yWmRzkZcJXKM1IeVYfF97mCgpz4”
- 通过
secret_key
解密 session- 反序列化成字典
from flask import Flask
from flask import render_template
from flask import request
app = Flask(__name__)
app.secret_key = "$^%%&%#$%&^*" # 设置密钥,如果不设置密钥,使用session会报错
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "GET":
return render_template("login.html")
elif request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
print(username, password)
if username == "jack" and password == "123456":
session["token"] = "asdihiasdhasdiuhaisdh"
return redirect("/")
@app.route("/")
def index():
token = session.get("token")
return "Hello world"
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
Flask Jinja2
-
{{ }}
引用变量数据 执行函数 -
{% %}
逻辑代码 -
for循环
{% for item in items %} # 逻辑代码 {% endfor %}
-
if 判断
{% if 判断条件 %} # 逻辑代码 {% else %} # 逻辑代码 {% endif %}
from flask import Flask
from flask import render_template
app = Flask(__name__)
def fun():
return "水花兄弟!"
@app.route("/name")
def user():
users = [
{"name": "库里", "age": 34, "gender": "男"},
{"name": "汤普森", "age": 32, "gender": "男"},
{"name": "格林", "age": 31, "gender": "男"}
]
return render_template("user.html", users=users, fun=fun)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<table>
<tr>
<td>name</td>
<td>age</td>
<td>gender</td>
</tr>
{% for user in users %}
<tr>
<td>{{ user.name }}</td>
<td>
{% if user.age >= 32 %}
{{ fun() }}
{% else %}
{{ user.age }}
{% endif %}
</td>
<td>{{ user.gender }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
注意
:
- 变量为dict类型时,可用
key.value
、dict[key]
或者dict.get(key)
获取值- 函数可作为变量传入后调用
Flask 路由参数
route()
中的参数
-
endpoint不能重复,对应视图函数 默认是视图函数名 视图函数与路由对应关系
url_for
反向创建url
-
methods 允许亲求的方式
["GET", "POST", "DELETE", "PUT"]
-
redirect_to 永久重定向 ,没有进入视图直接跳转
-
strict_slashes 是否严格要求路由匹配规则
为True时:“/url/“是不被允许的,只允许”/url”
为False时:"/url/“和”/url"都可以
-
default={"nid": 123}
默认参数,视图函数中func(nid)一定要接受传参
动态路由参数
- 接收多个参数
@app.route("/pages/<int:page>_<int:row>")
def pages(page, row):
print(page, row)
return "200 ok"
Flask实例化配置
template_folder
:模板存放路径,默认为templates
static_folder
:静态文件存放路径,默认值为static
static_url_path
:静态文件访问路径,默认值为/static
static_host
:静态文件访问服务HOST => 指向到另一台服务器
from flask import Flask
app = Flask(
__name__,
template_folder="templates",
static_folder="static",
static_url_path="/static",
static_host="xxxxx"
)
Flask对象Config配置
DEBUG
模式:开启编辑时代码重启,Log打印级别最低,错误信息透传
app.config["DEBUG"] = True
TESTING
模式:无限接近生产环境,代码编辑不会重启,Log级别较高,错误信息不再透传
app.config["TESTING"] = False
SESSION_COOKIE_NAME
:浏览器中session的名称,第一个字符不能是空格
app.config["SESSION_COOKIE_NAME"] = "session"
Flask Config快速配置
class DebugConfig:
DEBUG = True
SECRET_KEY = "asdhugoiasd^%$$#$%#&^%7"
from settings import DebugConfig
app = Flask(__name__)
# app.secret_key = "$^%%&%#$%&^*"
app.config.from_object(DebugConfig)
Flask 蓝图 BluePrint
蓝图作用:功能隔离,路由隔离
新建app01文件夹,下新增views.py文件、templates、static文件夹等
url_prefix
:表示URL前缀,用于隔离相同URL
user
:表示蓝图名称,不能重复,保证在app中是唯一的
template_folder
:默认是最外层项目templates,如需设置内部文件夹,则template_folder=app01/templates
# views.py
# 蓝图
from flask import Blueprint
# BluePrint 当作一个不能够run的Flask
user = Blueprint("user", __name__, url_prefix="/user")
@user.route("/login")
def login():
return "I am userBP"
在app.py中注册蓝图
from flask import Flask
app = Flask(__name__)
from app01.views import user
# 注册蓝图
app.register_blueprint(user)
if __name__ == "__main__":
app.run()
浏览器中访问app01的login视图:http://127.0.0.1:5000/user/login
Flask特殊装饰器
-
@app.before_request
请求进入视图函数之前
-
@app.after_request
:请求结束,返回响应时正常:before_request1 => before_request2 =>视图函数 => after_request2 => after_request1
异常:before_request1 => after_request2 => after_request1
-
@app.errorhandler
(4xx or 5xx):重定义错误信息@app.errorhandler(404) def error(error_message): print(error_message) # return f"你访问的{}不存在".format(request.path) # 自定义信息 # return redirect("www.baidu.com") # 重定向 return send_file("文件路径") # 返回图片或者其他文件
Flask CBV
from flask import Flask
app = Flask(__name__)
@app.route("/") # FBV
def index():
return "asbdj"
from flask import views
class Index(views.MethodView):
def get(self):
return "GET 200 OK"
def post(self):
return "POST 201 OK"
def put(self):
return "PUT 200 OK"
def delete(self):
return "DELETE 200 OK"
# 注册路由
app.add_url_rule("/user", view_func=Index.as_view(name="user"))
if __name__ == '__main__':
app.run(debug=True)
监听 端口
0.0.0.0
:监听ip地址
端口 - 监听应用程序
包到应用程序的过程:
IOS 5层中的最高层 ——————- 应用
服务器接收到数据:浏览器-9528 b"HTTP 1.1 /GET\r\n" 192.168.14.25:9527
网卡 => 操作系统
操作系统 解包: - 端口9527 - 收到 b"HTTP 1.1 /GET\r\n"
Flask -
WSGI
把数据转换成environ
对象,请求原始数据对象,几乎接近于request
对象Flask 接收到
WSGI
转换的environ对象request_class Flask Request
样式Flask
request
对象,如request.form
Flask-Session第三方组件
- 交由客户端保管机制 - 安全性相对较差 Cookie
- 原生Session优势是不占用服务器空间
- Flask-Session - DjangoSession相对一样的数据
- redis在内网中使用,不要放在公网
# 蓝图
from flask import Blueprint
from flask import session
# BluePrint 当作一个不能够run的Flask
user = Blueprint("user", __name__, url_prefix="/user")
@user.route("/login")
def login():
session["user"] = "asdugaiushduyh"
return "I am userBP"
@user.route("")
def index():
print(session["user"])
return "session"
from flask import Flask
from flask_session import Session
from redis import Redis
from app01.views import user
app = Flask(__name__)
app.secret_key = "asdhiuh989"
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_REDIS"] = Redis(host="106.15.**.**", port=6379, password="******")
# 配置之后
Session(app)
# 蓝图之前
app.register_blueprint(user)
if __name__ == "__main__":
app.run()
Flask请求上下文
该部分主要是阅读源码
-
线程安全
极快解决阻塞问题,保证公共对象的安全性,缺点是非常耗费内存,空间换时间
import time from copy import deepcopy from threading import Thread, get_ident class Foo: pass local_dict = {} f = Foo() f.num = 0 def add_(i): local_dict[get_ident()] = deepcopy(f) local_dict[get_ident()].num = i time.sleep(1) print(local_dict[get_ident()].num) for i in range(20): task = Thread(target=add_, args=(i,)) task.start()
import time from threading import Thread, local class Foo(local): pass f = Foo() f.num = 0 def add_(i): f.num = i time.sleep(1) print(f.num) for i in range(20): task = Thread(target=add_, args=(i,)) task.start()
相对使用线程ID保存数据的方式,使用local占用资源更少
-
偏函数:将原函数和原函数接收的参数一并返回新函数,在执行新函数时,将参数传入原函数中一并执行。
from functools import partial def abfunc(a, b, c): return a + b + c new_fum = partial(abfunc, c=88) print(new_fum(6, 7))
-
OOP 面向对象
熟悉掌握相关魔法方法以及重写,如
__setitem__
、__getitem__
等