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.routeapp.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()
posted @ 2020-01-04 16:00  正在学习的Barry  阅读(1580)  评论(0编辑  收藏  举报
-->