一、路由的本質
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()