flask之路由详解
一、路由的本质
flask的路由直接在函数上使用装饰器的话非常的乱,那可不可以想django一样把路由都写到一起呢。
查看源码,我们知道原来的路由就是一个装饰器,装饰器的第一个参数就是路由规则
实际上在装饰器内的闭包函数最终调用的是def add_url_rule(self,rule,endpoint=None,view_func=None,provide_automatic_options=None,**options):
'''
rule:路由规则
endpoint:反向解析别名,和CBV中as_view(name=...)的name是一样的。因为在源码内部,实际上也会去取参数view_func的__name__也就是指定的函数或类的名字
view_func:响应对象
'''
因此我们可以直接改写路由。
将原来装饰器修饰的路由,改写为使用add_url_rule
的路由
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "ok"
if __name__ == '__main__':
app.run()
改写为
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "ok"
# 路由匹配规则和响应函数
app.add_url_rule("/index", view_func=index)
if __name__ == '__main__':
app.run()
二、CBV
和django一样,flask也是有CBV的,都是通过as_view()来返回view对象,通过匹配然后执行view函数完成dispatch_request的响应方式的分发。
不同的是。as_view()中必须传递一个name,这个name就是作为反向解析的名字给类的__name__
flask导入的views.View类必须实现dispatch_request方法,不然匹配路由成功后执行view函数完成分发会报错。因为源码中必须让你实现
from flask import Flask,views
app = Flask(__name__)
#简单模式
class IndexView(views.View):
def dispatch_request(self):
print('Index')
return 'Index!'
app.add_url_rule('/index1', view_func=IndexView.as_view(name='index1')) # name=endpoint 反向解析的别名
if __name__ == '__main__':
app.run()
2.1 使用views.MethodView类
这个类已经帮我们实现好了dispatch_request
方法,就不需要自己再去写了。而他帮你实现的dispatch_request
方法中做的事和Django中的dispath方法中做的事情是一致的。都是通过反射去取响应的请求方式名同名的函数
from flask import Flask,views
app = Flask(__name__)
#通常用此方式
class IndexView(views.MethodView):
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index')) # name=endpoint
if __name__ == '__main__':
app.run()
2.2 指定响应的请求方式类型
无论是FBV还是CBV的实现方式都可以在函数内部或类内部添加methods = ['GET',"POST"]
.当app.add_url_rule
执行时会通过反射去函数或类内部的methods
中取指定的响应方法,如果没有写,默认只响应get方式请求。
但如果是CBV的方式,没有写methods
则和django一样去取类内部实现的方法。但如果写了methods
,只会响应methods
中的请求方式对应的方法
'''CBV'''
from flask import Flask,views
app = Flask(__name__)
class IndexView(views.MethodView):
methods = ['GET'] # 只响应GET请求对于的get方法
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index'))
if __name__ == '__main__':
app.run()
2.3 指定装饰器修饰
在使用CBV,添加对应的路由是需要通过as_view来得到view对象。而在as_view()内部,会根据类属性decorators=[auth, ]
来执行[]中对应的装饰器。也就是说,当程序执行时,进入as_view()后,会给该装饰器传入一个view对象,然后进入对应的装饰器,该装饰器必须返回一个view对象,否则就会报错。
传进去一个view,返回一个同名的view对象,这不就是装饰器嘛。给view加了一层装饰器,无论哪个路由匹配成功,都会先进入该装饰器。
from flask import Flask,views,url_for,jsonify,render_template
app = Flask(__name__)
def auth(view):
def inner(*args,**kwargs):
if view.__name__=="index":
res = view(*args,**kwargs)
else:
# res = render_template("404.html")
res = jsonify({"status": 0,"msg":"傻逼"})
return res
return inner
#通常用此方式
class IndexView(views.MethodView):
# 给dispatch_request加装饰器
decorators = [auth, ]
def get(self):
return 'Index.GET'
def post(self):
return 'Index.POST'
app.add_url_rule('/index', view_func=IndexView.as_view(name='index1'))
if __name__ == '__main__':
app.run()
三、路由参数
@app.route
和app.add_url_rule
参数:
'''
rule:路由规则
endpoint:反向解析别名,和CBV中as_view(name=...)的name是一样的。因为在源码内部,实际上也会去取参数view_func的__name__也就是指定的函数或类的名字
view_func:响应对象
defaults:默认值为None, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}
methods:允许的请求方式,如:["GET", "POST"]
strict_slashes: 对URL最后的 / 符号是否严格要求,默认等于None
redirect_to: 重定向到指定地址
'''
- url_for: 可以根据反向解析的别名来拿到对应路由
from flask import Flask,views,url_for
app = Flask(__name__)
@app.route("/index/",endpoint="a",methods=["POST","GET"],strict_slashes=None,redirect_to="/index2")
def index():
return "ok"
@app.route("/index2")
def index2():
# 打印反向解析的别名是a的路由
print(url_for("a"))
return "吃饭去了"
if __name__ == '__main__':
app.run()
四、路由转换器
路由转化器,其实就是django中路由的有名分组,通过参数名来得到路由的参数。区别是不用正则,但是可以指定参数类型。
from flask import Flask
app = Flask(__name__)
# 和有名分组一样,只不过不用正则了
@app.route("/<string:flag>")
def index(flag):
return f"jason is sb ? {flag}"
if __name__ == '__main__':
app.run()
默认路由转化器参数类型
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
4.1 自定义转换器
- 1 写类,继承BaseConverter,实现三个方法init、to_python、to_url
- 2 注册:
app.url_map.converters['regex'] = RegexConverter
- 3 使用:
@app.route('/index/<regex("\d+"):nid>')
正则表达式会当作第二个参数传递到类中
#1 写类,继承BaseConverter,实现三个方法init、to_python、to_url
#2 注册:app.url_map.converters['regex'] = RegexConverter
#3 使用:@app.route('/index/<regex("\d+"):nid>') 正则表达式会当作第二个参数传递到类中
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter
app = Flask(import_name=__name__)
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
"""
return int(value)+123
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
"""
# 先执行父类方法
val = super(RegexConverter, self).to_url(value)
return val+"json"
# 添加到flask中
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(nid)
print(url_for('index', nid='888')) # 使用反向解析,会执行自定义转换器的to_url
return 'Index'
if __name__ == '__main__':
app.run()