Flask-2-路由
前沿:处理URL和视图函数之间的绑定关系的程序就叫路由。
一、多个URL路由
一个函数可以设置多个URL路由规则
@app.route("/cases")
@app.route("/cases/<case_id>")
def get_case(case_id=None):
if case_id is not None:
return f"{case_id}"
return "cases"
这个例子接收两种URL,通过任何一个URL都可以走到这个视图函数里面
- 访问localhost:5000/cases:则返回cases
- 访问localhose:5000/case/432423,则返回432423
注意
1 、多个url可以绑定同一个视图函数,但是切记同一个url不能绑定在多个视图函数上
2、如果还有其他的装饰器怎么处理?
- 视图装饰器应该放最外层,否则里面的装饰器不会生效
- 视图函数包裹的装饰器不要return其他信息,只要返回我们视图函数即可,否则会被包装程返回数据。
def log_time(func):
def swapper(*args, **kwargs):
print(time.time())
# 千万不要return其他值,否则会被包装成响应对象,返回到前端页面
# return "自定义装饰器"
return func(*args, **kwargs)
return swapper
@app.route("/interface/")
@log_time
def get_interface():
return "interface"
# 注意前面说的自定义的装饰要放在url注册装饰器的下面,
# 如果位置换一下,你会发现自动义装饰器不会执行
装饰器就是在视图函数外,处理一些公共的方法,比如储存访问的次数
def set_count(func):
def swapper(*args, **kwargs):
app.config["num"]+=1
return func(*args, **kwargs)
return swapper
二、路由的动态参数
我们在设计url的时候,也可以设置成动态的url,前端通过url来传参给我们,不一定非要通过get,post请求传参
只需要把动态的url放在<>中如:@app.route("/cases/<case_id>"),他可以把前端访问的url动态部分,传到视图函数中,视图函数需要设置一个参数接收即可
@app.route("/cases/<case_id>")
def get_case(case_id=None):
if case_id is not None:
return f"{case_id}"
return "cases"
动态url参数接收类型
# int:整数
@app.route("/cases/<int:case_id>")
# float:小数
@app.route("/cases/<float:case_id>")
# string: 字符串,没有定义时默认类型
@app.route("/cases/<case_id>")
@app.route("/cases/<string:case_id>")
# path:路径 /34/2233/4324/,可以/收尾,其他的不能
@app.route("/cases/<path:case_id>")
三、路由重定向
比较访问,/index,和/index/ 两个路由之间的区别
1、/index :我们访问/index/的时候不能访问成功,会报错404
flask 哲学:当url 定义成/interface时, flask它会认为 /interface 和 /interface/ 是两个不同的url。
定义的/interface,不能通过/interface/访问。
2、/index/:我们定义这个时候,访问/index和/index/都能访问成功,但是两种是有区别的
flask 为了灵活 有另外一种方式兼容:永久重定向。
即:可以定义成/index/,此时 访问/index,flask没有找到,会告诉游览器,我找到了一个接近的 返回308,找到的url
定义在location中,然后游览器在访问 location中的 /index/ ,返回200.
3、为什么不进行这样的操作呢?
唯一url原则
- 从逻辑上来讲这样是不合理的,我们同样一个逻辑,你为什么要给两个地址?
- 对SEO有好处,两个url会被搜索两次,重复内容会被降权
- 到底后面加“/”,还是不加呢?加可以兼容,不加就报错,根据自己实际需求处理就好,我通常是不加的
四、路由注册机制
访问URL执法对应的视图函数,函数是没有主动调用的,在哪里使用的呢?
之前我们最小原型实例中,优化后我们是把url和函数处理逻辑映射到字典中保存,其实flask也是这一套操作
flask其实提供了两种方式注册路由
1、装饰器:@app.route("/")
@app.route("/")
def index():
pass
2、集中注册:app.add_url_rule()
def get_case_list():
return "case_list"
app.add_url_rule("/case_list", view_func=get_case_list)
3、源码阅读
其实我们看route()方法源码,也可以发现也是调用app.add_url_rule(),只是flask帮我实现了装饰器,在中小型项目中更加优雅方便
而url和视图的映射关系(通过端点-下面有提到)都是保存在url_map里面
4、装饰器VS集中注册
装饰器更加优雅,看起来更加清晰,url和视图函数关系明确,非常适合中小型项目
集中注册,更加灵活,把所有的url路由都定义到了一起,当某一个路由出现出现问题的适合,很快就能找到改路由,不能到各个视图函数文件中找,适合中大型项目
提示:其实falsk提供的所有装饰器,都有对应的集中注册方法,后续降到模板引擎用的和请求钩子等都可以采用集中方式,这里大家有个印象即可,后续分享到对应内容在具体说明
五、app.route()可配置参数
app.route()的option参数是用了werkzeug的Rule类
有很多参数可配置,也是我们经常会用的
我们主要掌握其中几个重点经常用的参数即可,其他如有需求,请自行阅读源码的参数说明进行使用
1、endpoint:端点
可以理解为url和视图函数绑定关系的命名,默认是None,flask会取视图函数的名称定义关系名,也可以自定义
进行URL构建的时候会用url_for(endpoint),endpoint的主要作用是在url和视图函数中间设置一个桥梁,通过这个endpoint就能找到对应的url和视图函数
@app.route("/home", endpoint="get_home_1")
def get_home():
return "home"
print(app.url_map())
2、methods:请求方法
默认没有设置的时候,仅支持GET、HEAD、OPTIONS
我们就通过这个参数来限制前端的接口的访问方法
# 定义post请求方法,前端访问只能用post方法请求
@app.route("/login/<username>", methods=["POST"])
def login(username=None):
if username is None:
return "请输入用户名"
return "登录成功"
3、redirect_to:重定向
我们在使用重定向的时候其实是有两种方式的,一是用redirect_to关键字参数,二是用redirect()方法实现。
两种究竟有何不同呢?
一、redirect_to关键字参数
使用关键字参数重定向到其他路由的时候,本视图函数的逻辑是不会执行的,只会执行重定向的路由的视图函数逻辑
@app.route("/cases/<path:case_id>")
def get_case(case_id=None):
if case_id is not None:
print(type(case_id))
return f"{case_id}"
return "cases"
# 当时定义参数redirect_to 参数时,访问"/" 不在返回index,返回重定向的路由case_id
@app.route("/",methods=["GET"],redirect_to="/cases/4444")
def index():
print("关键字参数重定向内部逻辑")
return "index"
二、redirec():方法
flask给我给提供了重定向其他路由的方法,接收url参数即可。
但是url这种参数有可能会变,而url和视图函数之间的绑定关系一般是不变的,也就是端点一般是不变的,所以redirect()方法配合url_for(endpoind)使用
1)url_for()
一般重定向的时候组合使用,url_for方法,传入端点名,它里面就帮我们实现了,通过端点找到url,而且还支持传关键字参数,假如重定向到interface接口时,需要一个关键字参数id=3,可以直接redirect(url_for("interface",id=3))当然直接使用url在url后面加?id=3 也可行
2)参数说明
endpoint:端点(默认函数的名字)
**values:URL的关键字参数
_external:如果设置为True,则生成一个绝对路径URL
_scheme:一个字符串指定所需的URL方案。_external参数必须设置为True,不然会抛出ValueError。
_anchor:如果设置了这个则给URL添加一个mao
_method: 如果设置这个则显示地调用这个HTTP方法
3)redirect+url_for案例
@app.route("/interface/")
def get_interface():
return "interface"
# 方式二,在视图函数内处理完逻辑在重定向
@app.route("/home")
def get_home():
print("函数内部逻辑")
# 得到完整的url,并且请求参数id=3
print(url_for("get_home_1", _external=True, id=3))
return redirect(url_for("get_interface", id=3))
4、defaults:参数默认值
定义默认参数,可以在视图函数中接收,defaults={"id":2}
也可以直接在视图函数中定义默认值参数def interface(id=2) ---更方便
@app.route("/cases/<case_id>")
def get_case(case_id):
return f"{case_id}"
如果我们请求的时候不传case_id 就会报错
怎么才能调整他不报错呢?
我们只要给路由增加一个defaults默认值即可
@app.route("/cases/", defaults={"case_id": 3})
@app.route("/cases/<case_id>")
def get_case(case_id):
return f"{case_id}"
我们也可以不同这个参数,可以直接在视图函数中定义参数的给默认值即可--更方便
@app.route("/cases/")
@app.route("/cases/<case_id>")
def get_case(case_id=3):
return f"{case_id}"
六、视图函数的分离(大型项目)
随着项目的增大,我们想要把视图函数放在一起,视图函数,app、url不写在一个文件里的,所以我们的架构变成了
- 启动文件:仅仅初始化app,配置信息,启动服务
- 视图函数:接收请求,处理数据、响应结果
- url集中注册:url和视图绑定
- 数据处理:为视图函数服务,被视图函数调用
- templates:html页面
- static:静态文件如js,css
- 其他的帮助函数:其他公共方法
# viewfunction.py 视图函数py文件
# form model层的数据处理方法
# 首页
def home():
# 接收请求数据
# 处理数据
return "home page"
# cases页
def cases():
return "case page"
# urls.py
# 导入app对象、导入视图函数,进行集中对象
#
from flask_main import app
import viewfunction as views
# 注册路由
app.add_url_rule("/", view_func=views.home)
app.add_url_rule("/cases", view_func=views.cases)
# flask_main.py
# 主要时初始化app,处理配置信息,开启服务即可
# 接下来导入urls.py注册的路由即可
from flask import Flask
# from flask_frame.Flask_views.urls import * # 直接引用会导致循环导入
app = Flask(__name__)
app.config["DEBUG"] = True
# 只能在哪里用的时候在导入
from flask_frame.Flask_route.flask_fenceng.urls import *
if __name__ == '__main__':
app.run(debug=app.config.get("DEBUG"))
上述只简单的分了视图、url、app分离,还有具体model层和其他公共方法之类根据业务实际分层即可
这样我们主入口功能其实非常明确的,也非常的简单
注意
不知道大家注意到没有?在我们的urls层需要到flask.main文件的app 来注册路由,而falsk.mian里面又需要加载urls层注册的路由,然后开启的服务才能加载上这些路由。
两个文件之间互相导入,肯定会导致循环导入的情况发生。
所以为了避免循环导入我们只能像案例中的那样,哪里用就在哪里导入,没必要非要遵守PEP8规范。其实很多三方框架都是这么处理,包括falsk也用过这种导入