Flask之路由系统
一、路由本质
在flask中:
from flask import Flask app = Flask(__name__) @app.route('/',methods=['GET','POST'],endpoint='index') def index(): return 'index' if __name__ == '__main__': app.run()
其中的视图函数被@app.route('/',methods=['GET','POST'],endpoint='index')装饰,在route函数中:
def route(self, rule, **options): def decorator(f): endpoint = options.pop("endpoint", None) self.add_url_rule(rule, endpoint, f, **options) return f return decorator
其中self就是app对象,那么执行顺序是:
1、app.route('/',methods=['GET','POST'],endpoint='index')
执行这一步主要就是通过闭包将route的参数传递到decorator的作用域中,执行route函数返回的就是decorator函数。
2、@decorator
当执行完第一步后,视图函数就会变成下面这样:
@decorator def index(): return 'index'
此时index相当于被decorator修饰,在decorator函数中,endpoint如果存在就取出来,如果没有传递默认为None;然后值得注意的是:
self.add_url_rule(rule, endpoint, f, **options) #rule是‘/’,f是index函数,**options是methods参数
这一步是将路由与视图加入到对应的映射关系中。
3、add_url_rule
用于添加路由与视图的对应关系,这也是flask中路由的本质,我们直接通过这个方法也是可以实现以上的功能:
from flask import Flask app = Flask(__name__) def index(): return 'index' app.add_url_rule('/', view_func=index, methods=['GET', 'POST'], endpoint='index') if __name__ == '__main__': app.run()
4、add_url_rule参数详解
add_url_rule中有很多参数,比如:
rule #url规则 view_func #视图名称 defaults = None #缺省值为None,当URL中无参数,函数需要参数时,使用defaults={'k':'v'}为函数提供参数 endpoint = None #缺省值为None,用于反向生成URL,即: url_for('名称') methods=None #缺省值为None,允许的请求方式,如:["GET","POST"] redirect_to=None #缺省值为None,重定向到指定地址 strict_slashes=None #缺省值为None,对URL最后的 / 符号是否严格要求
- defaults
from flask import Flask app = Flask(__name__) @app.route('/',endpoint='index',defaults={'id':10}) def index(id): print(id) #接收传递的默认参数 return 'index' if __name__ == '__main__': app.run()
- redirect_to
from flask import Flask app = Flask(__name__) @app.route('/index', methods=['GET', 'POST'], endpoint='index1', redirect_to="/index2") def index(): return 'index' @app.route('/index2', methods=['GET', 'POST'], endpoint='index2') def index2(): return 'index2' if __name__ == '__main__': app.run()
- strict_slashes
from flask import Flask app = Flask(__name__) @app.route('/index',strict_slashes=False) #访问http://127.0.0.1:5000/index 或http://127.0.0.1:5000/index/ 均可 def index(): return 'index' @app.route('/home',strict_slashes=True) #只能访问 http://127.0.0.1:5000/home def home(): return 'home' if __name__ == '__main__': app.run()
5、正则路由匹配
在Flask中的路由是有一些变量规则的,通过把 URL 的一部分标记为<variable_name> 就可以在 URL 中添加变量。标记的 部分会作为关键字参数传递给函数。通过使用 <converter:variable_name>,可以 选择性的加上一个转换器,为变量指定规则。请看下面的例子:
@app.route('/user/<username>') def show_user_profile(username): # show the user profile for that user return 'User %s' % escape(username) @app.route('/post/<int:post_id>') def show_post(post_id): # show the post with the given id, the id is an integer return 'Post %d' % post_id @app.route('/path/<path:subpath>') def show_subpath(subpath): # show the subpath after /path/ return 'Subpath %s' % escape(subpath)
转换器类型:
string | (缺省值) 接受任何不包含斜杠的文本 |
int | 接受正整数 |
float | 接受正浮点数 |
path | 类似 string ,但可以包含斜杠 |
uuid | 接受 UUID 字符串 |
但是如果自己想指定规则,那么可以这样做:
from flask import Flask, url_for from werkzeug.routing import BaseConverter import re app = Flask(__name__) # 1、自定义RegexConvert class RegecConvert(BaseConverter): def __init__(self, map, regex): super(RegecConvert, self).__init__(map) self.regex = regex def to_python(self, value): """进行路由匹配,匹配成功后返回给视图函数中的参数""" value = re.match(self.regex, value).group() return int(value) def to_url(self, value): """使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数""" val = super(RegecConvert, self).to_url(value) return val # 2、将RegecConvert加入到flask中 app.url_map.converters['regex'] = RegecConvert # 下次再使用该规则,就使用regex,这与int/float意思一样 @app.route('/index/<regex("\d+"):id>', endpoint='index') def index(id): print(id, type(id)) # 这个id是经过to_python方法匹配处理后的结果 url_for('index', id=10) # 这个id的值传递给to_url方法进行处理 return 'index' if __name__ == '__main__': app.run()
二、CBV
from flask import Flask, views app = Flask(__name__) def auth(func): def inner(*args, **kwargs): result = func(*args, **kwargs) return result return inner class IndexView(views.MethodView): method = ['GET', ] decorators = [auth, ] def get(self): return 'get' def post(self): return 'post' app.add_url_rule('/', view_func=IndexView.as_view(name='index')) if __name__ == '__main__': app.run()
启动web服务在5000端口,然后访问‘/’路径,就会执行IndexView.as_view(name='index'),显然会去继承的父类中寻找:
class View(object): methods = None provide_automatic_options = None decorators = () @classmethod def as_view(cls, name, *class_args, **class_kwargs): def view(*args, **kwargs): self = view.view_class(*class_args, **class_kwargs) return self.dispatch_request(*args, **kwargs) if cls.decorators: view.__name__ = name view.__module__ = cls.__module__ for decorator in cls.decorators: view = decorator(view) view.view_class = cls view.__name__ = name view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods view.provide_automatic_options = cls.provide_automatic_options return view
可以看到执行的顺序:as_view-->view-->dispatch_request,在dispatch_request进行反射和分发。
class MethodView(with_metaclass(MethodViewType, View)): def dispatch_request(self, *args, **kwargs): meth = getattr(self, request.method.lower(), None) # 反射 # If the request method is HEAD and we don't have a handler for it # retry with GET. if meth is None and request.method == "HEAD": meth = getattr(self, "get", None) assert meth is not None, "Unimplemented method %r" % request.method return meth(*args, **kwargs)
至于IndexView中的decorators相当于装饰器,可以看到源码中进行循环所有的装饰器函数分别执行再赋值。
作者:iveBoy
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。