当前位置:   article > 正文

【3.0】flask之路由系统

flask3.0 route

【一】路由系统基于装饰器

  1. from flask import Flask
  2. app = Flask(__name__)
  3. # (1) flask 的路由系统基于装饰器
  4. # rule : 路径
  5. # methods : 请求方式【列表】
  6. # endpoint :别名
  7. # @app.route('/detail/<int:nid>',methods=['GET'],endpoint='detail')
  8. @app.route('/index', methods=['GET'])
  9. def index():
  10. return 'hello world'
  11. if __name__ == '__main__':
  12. app.run()

【二】转换器

  • 默认转化器
  1. DEFAULT_CONVERTERS = {
  2. 'default': UnicodeConverter,
  3. 'string': UnicodeConverter,
  4. 'any': AnyConverter,
  5. 'path': PathConverter,
  6. 'int': IntegerConverter,
  7. 'float': FloatConverter,
  8. 'uuid': UUIDConverter,
  9. }
  1. from flask import Flask
  2. app = Flask(__name__)
  3. # (2) 默认转换器
  4. '''
  5. DEFAULT_CONVERTERS = {
  6. 'default': UnicodeConverter,
  7. 'string': UnicodeConverter,
  8. 'any': AnyConverter,
  9. 'path': PathConverter,
  10. 'int': IntegerConverter,
  11. 'float': FloatConverter,
  12. 'uuid': UUIDConverter,
  13. }
  14. '''
  15. # 常用 string path int
  16. # @app.route('/index/<string:name>', methods=['GET'])
  17. @app.route('/index', methods=['GET'])
  18. def index(name):
  19. return 'hello world'
  20. if __name__ == '__main__':
  21. app.run()

【三】路由系统的本质

【1】执行流程分析

  1. from flask import Flask
  2. app = Flask(__name__)
  3. # (3)路由系统的本质
  4. # @app.route('/index', methods=['GET']) 本质上是一个装饰器
  5. # 执行时 ---> index = @app.route('/index', methods=['GET'])(index)
  6. @app.route('/index', methods=['GET'])
  7. def index():
  8. return 'hello world'
  9. if __name__ == '__main__':
  10. app.run()
  • 执行@app.route('/index', methods=['GET'])
  • 本质上执行了index = @app.route('/index', methods=['GET'])(index)
  • 触发了 route 方法中的 decorator
  1. def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
  2. def decorator(f: T_route) -> T_route:
  3. endpoint = options.pop("endpoint", None)
  4. self.add_url_rule(rule, endpoint, f, **options)
  5. return f # f 是视图函数 ,在这里并没有对视图函数进行额外的处理,只是加了一些参数
  6. return decorator
  • 所以本质上执行了index = decorator(index)

【2】分析 decorator 函数

  1. def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
  2. def decorator(f: T_route) -> T_route:
  3. endpoint = options.pop("endpoint", None)
  4. self.add_url_rule(rule, endpoint, f, **options)
  5. return f
  6. return decorator
  • f 是视图函数 ,在这里并没有对视图函数进行额外的处理,只是加了一些参数
  1. endpoint = options.pop("endpoint", None)
  2. self.add_url_rule(rule, endpoint, f, **options)
  • 视图函数执行 @app.route('/index', methods=['GET'])

    • endpoint 携带在 route 内
    • 从 options 中弹出 endpoint ,有则弹出,无则 None
  • 核心 self.add_url_rule(rule, endpoint, f, **options)

    • self 就是 app 对象
    • app.add_url_rule('路由地址', '路由的别名', '视图函数', '其他参数')
  1. from flask import Flask
  2. app = Flask(__name__)
  3. # @app.route('/index', methods=['GET'])
  4. def index():
  5. return 'hello world'
  6. # 自定义注册路由和视图
  7. app.add_url_rule('/index', 'index', index, '其他参数')
  8. if __name__ == '__main__':
  9. app.run()

【3】add_url_rule 的参数详解

  1. # URL规则
  2. rule
  3. # 视图函数名称
  4. view_func
  5. # 默认值, 当URL中无参数,函数需要参数时,使用defaults = {'k': 'v'}为函数提供参数
  6. defaults = None
  7. # 名称,用于反向生成URL,即: url_for('名称')
  8. endpoint = None
  9. # 允许的请求方式,如:["GET", "POST"]
  10. methods = None,
  11. #对URL最后的 / 符号是否严格要求
  12. strict_slashes = None
  13. '''
  14. @app.route('/index', strict_slashes=False)
  15. #访问http://www.xx.com/index/ 或http://www.xx.com/index均可
  16. @app.route('/index', strict_slashes=True)
  17. #仅访问http://www.xx.com/index
  18. '''
  19. #重定向到指定地址
  20. redirect_to = None,
  21. '''
  22. @app.route('/index/<int:nid>', redirect_to='/home/<nid>')
  23. '''
  24. #子域名访问
  25. subdomain = None,
  26. '''
  27. #C:\Windows\System32\drivers\etc\hosts
  28. 127.0.0.1 www.liuqingzheng.com
  29. 127.0.0.1 admin.liuqingzheng.com
  30. 127.0.0.1 buy.liuqingzheng.com
  31. from flask import Flask, views, url_for
  32. app = Flask(import_name=__name__)
  33. app.config['SERVER_NAME'] = 'liuqingzheng.com:5000'
  34. @app.route("/", subdomain="admin")
  35. def static_index():
  36. """Flask supports static subdomains
  37. This is available at static.your-domain.tld"""
  38. return "static.your-domain.tld"
  39. #可以传入任意的字符串,如传入的字符串为aa,显示为 aa.liuqingzheng.com
  40. @app.route("/dynamic", subdomain="<username>")
  41. def username_index(username):
  42. """Dynamic subdomains are also supported
  43. Try going to user1.your-domain.tld/dynamic"""
  44. return username + ".your-domain.tld"
  45. if __name__ == '__main__':
  46. app.run()
  47. 访问:
  48. http://www.liuqingzheng.com:5000/dynamic
  49. http://admin.liuqingzheng.com:5000/dynamic
  50. http://buy.liuqingzheng.com:5000/dynamic
  51. '''

【4】 endpoint 详解

(1)自解版

  • 当我们在视图函数中不传 endpoint 时,会走以下流程
  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/', methods=['GET'])
  4. def index():
  5. return 'hello world'
  6. if __name__ == '__main__':
  7. app.run()
  • 触发 decorator 方法
  1. def decorator(f: T_route) -> T_route:
  2. endpoint = options.pop("endpoint", None)
  3. self.add_url_rule(rule, endpoint, f, **options)
  4. return f
  • 此时 endpoint 是None
  • 接着会触发 Falsk 父类中的 add_url_rule 方法
  1. def add_url_rule(
  2. self,
  3. rule: str,
  4. endpoint: str | None = None,
  5. view_func: ft.RouteCallable | None = None,
  6. provide_automatic_options: bool | None = None,
  7. **options: t.Any,
  8. ) -> None:
  9. if endpoint is None:
  10. endpoint = _endpoint_from_view_func(view_func) # type: ignore
  11. options["endpoint"] = endpoint
  12. methods = options.pop("methods", None)
  • 此时会走到 endpoint = _endpoint_from_view_func(view_func) 方法
  1. def _endpoint_from_view_func(view_func: t.Callable) -> str:
  2. """Internal helper that returns the default endpoint for a given
  3. function. This always is the function name.
  4. """
  5. assert view_func is not None, "expected view func if endpoint is not provided."
  6. return view_func.__name__
  • 此时的 view_func 就是我们的视图函数

    • 返回 视图函数的吗,名字作为 endpoint
  • 由此也就解释了为什么在使用装饰器时,需要指定 endpoint 参数

    • 如果指定 endpoint 参数,就会按照我们指定的参数进行逻辑判断

      • 如果不指定 endpoint 参数,每次视图函数都会将 装饰器的 inner 传入

      • 所有的 endpoint 参数都是一样的,从而引发了 ennpoint 错误

      1. def add_url_rule(
      2. self,
      3. rule: str,
      4. endpoint: str | None = None,
      5. view_func: ft.RouteCallable | None = None,
      6. provide_automatic_options: bool | None = None,
      7. **options: t.Any,
      8. ) -> None:
      9. raise NotImplementedError
    • 也可以是用 wrapper 包装我们的装饰器

      • 包装后的装饰器就是我们的函数本身,而不再是inner函数

(2)参考版

在Flask中,当我们在视图函数中不传递endpoint参数时,会按照一定的流程生成默认的endpoint。下面我们来详细解释这个过程。

  1. 首先,我们定义一个Flask应用和一个路由装饰器@app.route('/'),该装饰器将视图函数index()与根路由'/'进行关联。
  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/', methods=['GET'])
  4. def index():
  5. return 'hello world'
  6. if __name__ == '__main__':
  7. app.run()
  1. 当使用装饰器时,会触发装饰器方法decorator,其中会获取options中的endpoint参数(如果存在),如果不存在则为None
  1. def decorator(f: T_route) -> T_route:
  2. endpoint = options.pop("endpoint", None)
  3. self.add_url_rule(rule, endpoint, f, **options)
  4. return f
  1. 接着,会调用Flask父类中的add_url_rule方法,该方法用于将路由规则和视图函数进行关联。在这个过程中,会检查传入的endpoint参数是否为None,如果是None,则会通过视图函数名称来生成默认的endpoint
  1. def add_url_rule(
  2. self,
  3. rule: str,
  4. endpoint: str | None = None,
  5. view_func: ft.RouteCallable | None = None,
  6. provide_automatic_options: bool | None = None,
  7. **options: t.Any,
  8. ) -> None:
  9. if endpoint is None:
  10. endpoint = _endpoint_from_view_func(view_func) # type: ignore
  11. options["endpoint"] = endpoint
  12. methods = options.pop("methods", None)
  1. _endpoint_from_view_func方法中,它会根据视图函数来生成默认的endpoint。如果视图函数为空,则会抛出异常;否则,会返回视图函数的名称作为默认的endpoint
  1. def _endpoint_from_view_func(view_func: t.Callable) -> str:
  2. assert view_func is not None, "expected view func if endpoint is not provided."
  3. return view_func.__name__
  1. 这就解释了为什么在使用装饰器时,有时需要通过指定endpoint参数来避免endpoint错误的发生。如果不指定endpoint参数,每次视图函数都会将装饰器内部函数传入add_url_rule方法作为view_func参数,并生成相同的endpoint。这样就会导致所有路由都具有相同的endpoint,进而引发endpoint冲突异常。

为了避免这种情况,我们可以使用wrapper函数包装装饰器。这样包装后的装饰器本身就是我们定义的函数,并且不再是内部函数。通过这种方式,每个路由装饰器都会拥有自己独立的endpoint,并且不会产生冲突。

  1. from flask import Flask
  2. app = Flask(__name__)
  3. def my_decorator(f):
  4. @app.route('/', methods=['GET'], endpoint='my_endpoint')
  5. def wrapper():
  6. return f()
  7. return wrapper
  8. @my_decorator
  9. def index():
  10. return 'hello world'
  11. if __name__ == '__main__':
  12. app.run()
  • 通过以上的解释和扩充,我们更加详细地了解了在Flask中生成默认endpoint的过程,并介绍了避免endpoint冲突的方法。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/191289
推荐阅读
相关标签
  

闽ICP备14008679号