Flask路由和视图

Flask路由和视图

一、 路由系统

1. 路由系统基础

  • 路由装饰器

    • Flask使用装饰器@app.route来将URL规则绑定到视图函数上。

    • 装饰器可以指定路径规则(rule)、请求方法(methods)、以及别名(endpoint)等。

  • 转换器

    • Flask默认支持多种路径转换器,如intfloatstring等,用于将URL中的变量部分转换为不同的数据类型。

    • 转换器使得URL可以动态匹配,并允许开发者定义复杂的URL规则。

    • python 复制代码
      DEFAULT_CONVERTERS = {
       'default':          UnicodeConverter,
       'string':           UnicodeConverter,
       'any':              AnyConverter,
       'path':             PathConverter,
       'int':              IntegerConverter,
       'float':            FloatConverter,
       'uuid':             UUIDConverter,
      }

2. 执行流程分析

  1. 路由装饰器

    • @app.route 是一个装饰器,使用它来将一个函数绑定到一个URL规则上。

    • 当装饰器被触发时,它实际上执行了 index = decorator(index),其中 decoratorroute 方法返回的内部函数。

    • decorator 函数是 route 方法的内部函数,它接收视图函数 f 作为参数,并使用 add_url_rule 方法将视图函数注册到路由上。

    python 复制代码
    def route(self, rule: str, **options: t.Any) -> t.Callable[[T_route], T_route]:
        def decorator(f: T_route) -> T_route:
            endpoint = options.pop("endpoint", None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator
  2. add_url_rule参数

    • rule:定义URL规则。
    • view_func:视图函数。
    • defaults:默认值,用于URL中没有参数但视图函数需要参数的情况。
    • endpoint:用于反向生成URL的名称。
    • methods:允许的HTTP请求方法。
    • strict_slashes:是否严格要求URL末尾的斜杠。
    • redirect_to:重定向到指定地址。
    • subdomain:子域名访问。

3. Endpoint

  1. 默认Endpoint生成
    • 如果在装饰器中没有指定endpoint,Flask会默认使用视图函数的名称作为endpoint
    • 如果存在多个路由使用了相同的视图函数,但没有指定不同的endpoint,会导致endpoint冲突。
  2. 避免Endpoint冲突
    • 可以在装饰器中明确指定endpoint参数,以避免冲突。
    • 使用自定义装饰器,通过包装视图函数来确保每个路由都有唯一的endpoint

二、 视图函数

1. FBV + 装饰器

  • 示例

    python 复制代码
    from functools import wraps
    from flask import Flask
    
    app = Flask(__name__)
    
    def outer(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器')
            return func(*args, **kwargs)
        return inner
    
    @app.route('/')
    @outer
    def index():
        return '根路径'
    
    @app.route('/home')
    @outer
    def home():
        return '主页'
    
    if __name__ == '__main__':
        app.run()
  • 说明:为什么在FBV中使用装饰器时,需要使用@wraps(func)或指定endpoint

    • 使用 @wraps(func)
      • 保持被装饰函数的__name____doc__属性,这样当查看inner函数时,它看起来就像是原始的func函数。
      • 这对于调试和文档来说非常重要,因为它们依赖于函数的名称和文档字符串。
    • 指定 endpoint
      • 通过指定endpoint,可以确保即使装饰器改变了函数的元信息,也可以通过端点名称来引用这个视图函数。

2. CBV + 装饰器

  • 示例

    python 复制代码
    from functools import wraps
    from flask import Flask
    from flask.views import MethodView
    
    app = Flask(__name__)
    
    def outer1(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器1')
            return func(*args, **kwargs)
        return inner
    
    def outer2(func):
        @wraps(func)
        def inner(*args, **kwargs):
            print('走了装饰器2')
            return func(*args, **kwargs)
        return inner
    
    
    class Home(MethodView):
        decorators = [outer1, outer2]
        methods = ['GET', "POST"]
    
        def get(self):
            return "GET"
    
        def post(self):
            return "POST"
    
    app.add_url_rule('/home', endpoint='home', view_func=Home.as_view('home'))
  • 说明

    • decorators = 装饰器1,
      • 如果有多个可以直接 逗号隔开
    • methods = 'GET', "POST"
      • 可以控制整个视图函数的请求方式

3. CBV源码解析

as_view 方法关键步骤:

  1. 检查 init_every_request 属性
    • 如果init_every_requestTrue,则每次请求都会创建类的新实例。
    • 如果为False,则类在第一次调用as_view时被实例化,并且这个实例在后续请求中被重用。
  2. 定义视图函数 view
    • 视图函数内部调用self.dispatch_request来处理请求。
  3. 支持异步操作
    • 使用current_app.ensure_sync确保视图函数和dispatch_request是同步的,即使它们可能是异步的。
  4. 应用装饰器
    • 如果类定义了decorators属性,这些装饰器会被应用到视图函数上。
    • 每个装饰器通过接收视图函数作为参数并返回一个新的函数来包装视图函数。
  5. 设置视图函数的元信息
    • 视图函数的__name____module____doc__属性被设置为类的对应属性,以便于调试和文档。
  6. 返回视图函数
    • 最终,as_view返回一个完全配置好的视图函数,它可以直接注册到路由中。
python 复制代码
def as_view(cls, name, *class_args, **class_kwargs):
 # 如果init_every_request为True,则每次请求都会创建类的新实例。
 # 如果为False,则类在第一次调用as_view时被实例化,并且这个实例在后续请求中被重用。
 if cls.init_every_request:
     def view(**kwargs):
         self = view.view_class(*class_args, **class_kwargs)
         # 支持异步操作
         return current_app.ensure_sync(self.dispatch_request)(**kwargs)

 else:
     self = cls(*class_args, **class_kwargs)
     def view(**kwargs):
         # 支持异步操作
         return current_app.ensure_sync(self.dispatch_request)(**kwargs)
 # 应用装饰器
 if cls.decorators:
     view.__name__ = name
     view.__module__ = cls.__module__
     for decorator in cls.decorators:
         view = decorator(view)
 # 设置视图函数的元信息        
 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

dispatch_request 方法

  1. 获取请求方法
    • 通过request.method.lower()获取请求方法的字符串表示,并将其转换为小写。
  2. 查找对应的方法
    • 尝试获取与请求方法同名的类方法(例如,如果请求是GET,则查找get方法)。
    • 如果请求是HEAD,但没有对应的处理方法,它会尝试使用GET方法。
  3. 执行方法
    • 如果找到了对应的方法,它会使用current_app.ensure_sync来确保该方法同步执行,并传入请求的参数。
  4. 抛出异常
    • 如果没有找到对应的方法,dispatch_request会抛出NotImplementedError
PYTHON 复制代码
def dispatch_request(self, **kwargs):
 # 使用getattr函数获取请求方法
 meth = getattr(self, request.method.lower(), None)

 # 如果请求方法未找到,且请求方法为"HEAD",则使用getattr函数获取"get"方法
 if meth is None and request.method == "HEAD":
     meth = getattr(self, "get", None)

 # 断言请求方法不为空,若为空则抛出异常
 assert meth is not None, f"Unimplemented method {request.method!r}"
 return current_app.ensure_sync(meth)(**kwargs)
相关推荐
love530love11 小时前
LiveTalking 数字人项目 Windows 部署完全指南(EPGF 架构)
人工智能·windows·python·架构·livetalking·epgf
遇事不決洛必達11 小时前
【Python基础】GIL 锁是什么及其对爬虫的影响
爬虫·python·线程·进程·gil锁
星辰徐哥11 小时前
Spring Boot 微服务架构设计与实现
spring boot·后端·微服务
星辰徐哥11 小时前
Spring Boot 数据导入导出与报表生成
spring boot·后端·ui
明夜之约11 小时前
Spring Boot 自动装配源码
java·spring boot·后端
Leaton Lee11 小时前
Spring Boot分层架构详解:从Controller到Service再到Mapper的完整流程
java·spring boot·后端·架构
Micro麦可乐11 小时前
Spring Boot 实战:从零设计一个短链系统(含完整代码与数据库设计)
数据库·spring boot·后端·哈希算法·雪花算法·短链系统
Jinkxs11 小时前
Resilience4j- 与 Spring Boot 快速集成:自动配置与基础注解使用
java·spring boot·后端
毕设源码_郑学姐11 小时前
计算机毕业设计springboot网络相册设计与实现 基于Spring Boot框架的在线相册管理系统开发与应用 Spring Boot驱动的网络影集设计与实践
spring boot·后端·课程设计
辣机小司11 小时前
【踩坑记录:Spring Boot 配置文件读取值不一致?警惕 YAML 的“八进制陷阱”与 SnakeYAML 版本之谜】
java·spring boot·后端·yaml·踩坑记录