flask之路【第一篇】请求、认证、路由
目录
⚙️本教程工程文件{ProjectName}:Practice_flask_0618
{ProjectName}:Practice_flask_0619
一、flask快速应用
pip install flask
1.1 flask介绍
-
flask框架基于 Werkzeug的wsgi(web服务网关接口)实现,flask自己没有wsgi
-
用户请求一旦到来就会执行
app.__call__
方法 -
写flask标准流程:
from flask import Flask
app=Flask(__name__)
@app.route("/login",endpoint="lgn") #endpoint为视图函数定义别名,如果不写那endpoint默认等于下边的视图函数名字。
def loggin():
print("登录中")
return "登录了"
@app.route("/index")
def index():
return "首页"
if __name__ =="main":
app.run()
💡路由中的endpoint参数为视图函数定义别名,如果不写那endpoint默认等于下边的视图函数名字。
1.2 用户登录示例
主程序
from flask import Flask, request, render_template, redirect, url_for
app = Flask(__name__)
DATA_DIC = {1: {"name": "xx", "age": 79}, 2: {"name": "yy", "age": 9}}
@app.route('/login', methods=['GET', 'POST'])
def login(): # 登录
if request.method == "GET":
return render_template("login.html")
user = request.form['username']
pwd = request.form['pwd']
if user == "张三" and pwd == "123":
return redirect("/index")
error = "用户名或密码错误"
return render_template("login.html", error=error)
@app.route('/index', endpoint="idx")
def index(): # 显示信息
data_dict = DATA_DIC
return render_template("index.html", data_dict=data_dict)
@app.route('/edit', methods=['GET', 'POST'])
def edit(): # 编辑信息
nid = request.args.get("nid")
nid = int(nid)
if request.method == "GET":
info = DATA_DIC[nid]
return render_template("edit.html", info=info)
username = request.form['user']
age = request.form['age']
DATA_DIC[nid] = {"name": username, "age": age}
return redirect("/index")
@app.route('/delete/<int:nid>')
def delete(nid): # 删除信息
del DATA_DIC[nid]
return redirect(url_for("idx"))
if __name__ == '__main__':
app.run()
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>Login</h1>
<form method="post">
<!--label>Username</label-->
<input type="text" name="username" placeholder="用户名">
<input type="password" name="pwd" placeholder="密码">
<input type="submit" value="登录"><span style="color: red;">{{ error }}</span>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index</title>
</head>
<body>
<table border="1px" style="margin-top: 10px;border: aqua">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for key,value in data_dict.items() %}
<tr>
<td>{{ key }}</td>
<td>{{ value.name }}</td>
<td>{{ value.age }}</td>
<td>
<a href="/edit?nid={{ key }}">编辑</a>
<a href="/delete/{{ key }}">删除</a>
</td>
</tr>
{% endfor %}
</tr>
</tbody>
</table>
</body>
</html>
edit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Edit</title>
</head>
<body>
<form method="post">
<input type="text" name="user" value="{{ info.name }}" >
<input type="text" name="age" value="{{ info.age }}" >
<input type="submit" value="提交" >
</form>
</body>
</html>
二、Http请求与响应
1.GET请求和post请求
(get请求是URL传参)(post请求有两种传参一种是:赋值的形式,如xx=13&yy=999;一种是字典下面会介绍到)
from flask import Flask, request,jsonify
app = Flask(__name__)
# http://127.0.0.1:5000/index
# http://127.0.0.1:5000/index?age=15&pwd=123
@app.route('/index', methods=['GET', 'POST'])
def index(): # put application's code here
# url 传参,get请求
age = request.args.get('age')
pwd = request.args.get('pwd')
print(age, pwd)
# post请求,请求体传参:xx=13&yy=999(请求体也可以用json格式),通过request.form.get获取参数
xx = request.form.get("xx")
yy = request.form.get("yy")
print(xx, yy)
# 也可以即在请求体中传参,也可以同时在请求体中传参
print(request.json)
return '成功'
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
2.POST请求(json的形式,用jsonify序列化)
import hashlib
from flask import Flask, request,jsonify
# import request
app = Flask(__name__)
def get_user_dict():
user_dict = {}
with open('db.txt', 'r') as f:
for line in f:
line = line.strip()
token, name = line.split(',')
user_dict[token] = name
return user_dict
@app.route('/bili', methods=['POST'])
def bili(): # put application's code here
'''
请求的数据格式要求(字典的形式post过来):{"ordered_string":"...."}
:return:进行数字化签名用户的序列字符串
'''
# print(request.json)
ordered_string=request.json['ordered_string']
if not ordered_string:
return jsonify({"status": False, "erro": "参数错误"})
# 数字化sign签名
encrypt_string=ordered_string + "56a5a382-b354-47a4-a0b2-ef40a9420cae"
sign =hashlib.md5(encrypt_string.encode('utf-8')).hexdigest()
return jsonify({"status": True, "data":sign})
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=True)
三、session
保存用户对话信息(session登录)
- session的用法:
#主程序中写入:
app.secret_key = "ss2d5fp8k9d33ll5896633" # 设置密钥以便用户session时存储cookie
#或者这种写法app.config['SECRET_KEY'] = "ss2d5fp8k9d33ll5896633"
#其他地方获取了user_dict信息以后,判断是否有值
if user_dict:
# 登录成功+跳转+存储用户session信息
session['user_info'] = user_dict
return redirect("/index")
@od_bp.route('/index')
def index():
# 读取cookie&解密用户信息
user_info = session.get("user_info")
print(user_info)
if not user_info: # 如果cookie中的session过期了或者没登陆,这个时候user_info是空的,要重定向到登录页面
return redirect("/login")
return "订单列表"
四、路由及相关参数
1路由的别名和url_for()的用法
@app.route('/index',endpoint="idx")
def index(): # put application's code here
data_dict = DATA_DIC
return render_template("index.html", data_dict=data_dict)
url_for("idx")
-
通过endpoint可以为路由定义别名
-
通过url_for("idx")可以获得路由地址,通过视图函数也可以获得即url_for("index")
url_for("idx") url_for("index") #index指的是index()函数的名字
url_for方法用于生成指定视图函数的URL。它接受视图函数的名称作为参数,并返回该视图函数对应的URL。url_for方法可以接受其他参数,用于生成带有参数的URL。
2 html向路由函数传值的方法
- href="/edit?nid={{ key }},通过get请求的方法将值传入
@app.route('/edit') # 路由获取方法一中传来的值
def edit(): # put application's code here
nid = request.args.get("nid")
return render_template("index.html")
- href="/delete/{{ key }}" ,通过url变量转换器
<int:arg>
的方法(int可以换成其他的参数如string、any等参数类型)
@app.route('/delete/<int:nid>') # 路由获取方法二中传来的值
def delete(nid): # put application's code here #注意此处视图函数要接收参数
del DATA_DIC[nid]
return redirect(url_for("idx")) #重定向的用法
html文件:
<table border="1px" style="margin-top: 10px;border: aqua">
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for key,value in data_dict.items() %} # for循环的用法
<tr>
<td>{{ key }}</td>
<td>{{ value.name }}</td>
<td>{{ value.age }}</td>
<td>
<a href="/edit?nid={{ key }}">编辑</a> # 方法一
<a href="/delete/{{ key }}">删除</a> # 方法二
</td>
</tr>
{% endfor %}
</tr>
</tbody>
</table>
✅总结
-
flask路由
@app.route('/login', methods=['GET', 'POST']) def login(): # 登录 pass
-
路由的参数
@app.route('/login', methods=['GET', 'POST'],endpoint="loginin") def login(): # 登录 pass # 注意:endpoint不能重名
-
动态路由
@app.route('/index') def index(): pass @app.route('/index/<name>') def index(name): pass @app.route('/index/<int:nid>') def index(nid): pass
-
获取提交的数据
from flask import request @app.route('/index') def index(): request.args #GET形式传递的参数 request.form #post形式提交的参数
-
返回数据
@app.route('/index') def index(): return render_template("模板文件") return jsonify return redirect("/index/") return redirect(url_for("idx")) return "...."
-
模板处理
{{ x }} {% for item in list %} {{item}} {% endfor %}
五、装饰器和实现用户认证登录
1.装饰器的知识点。
# 定义登录验证装饰器函数
# 使用@functools.wraps 使得endpoint不重复,不然返回的__name__全是decorated:详细解释是:
# 如果不加@functools.wraps,auth 装饰器定义了一个内部函数 decorated,应用装饰器时,实际上发生的是 原始 delete 函数被传递给 auth,auth 返回的是 decorated 函数。在装饰后:原始函数名 delete 实际上被替换成了 decorated;但 Flask 的路由装饰器 @app.route 仍然看到的是函数名 delete;然而在内部,这个函数实际上已经被替换为 decorated
# 如果你有多个使用 @auth 装饰的路由,它们都会被命名为 decorated
def auth(func):
@functools.wraps(func)
def decorated(*args, **kwargs):
if not session.get('user_info'):
return redirect(url_for("login"))
return func(*args, **kwargs)
return decorated
#进入删除页面,使用装饰器验证用户是否登录,即是否有session值
#@auth其实是语法糖,相当于 func = auth(func)
@app.route('/delete/<int:nid>')
@auth
def delete(nid): # put application's code here
del DATA_DIC[nid]
return redirect(url_for("index"))
2.拦截器实现用户认证登录(请求钩子)
from flask import Flask,redirect,session,request
def auth(): # 拦截器函数
if request.path.startswith("/static"):
# 不让拦截器拦截静态文件
return
if request.path=='/login':
# 继续执行不拦截
return
if session.get('user_info'):
# 继续执行不拦截
return
else:
return redirect("/login")
def get_real_name():
userinfo = session.get('user_info')
return userinfo["real_name"]
def create_app():
app = Flask(__name__)
app.secret_key = "ss2d5fp8k9d33ll5896633" # 设置密钥以便用户session时存储cookie
from .views import account
from .views import order
# 注册蓝图
app.register_blueprint(account.ac_bp)
app.register_blueprint(order.od_bp)
# 注册全局模板,传入每个模板可以调用的函数
app.template_global()(get_real_name)
app.before_request(auth) # 拦截器的应用,请求钩子调用拦截器
return app
✅两者比较拦截器更实用简单一点