flask详解--app.py (1)

app.py (1)

from __future__ import annotations

import collections.abc as cabc
import os
import sys
import typing as t
import weakref
from datetime import timedelta
from inspect import iscoroutinefunction
from itertools import chain
from types import TracebackType
from urllib.parse import quote as _url_quote

import click
from werkzeug.datastructures import Headers
from werkzeug.datastructures import ImmutableDict
from werkzeug.exceptions import BadRequestKeyError
from werkzeug.exceptions import HTTPException
from werkzeug.exceptions import InternalServerError
from werkzeug.routing import BuildError
from werkzeug.routing import MapAdapter
from werkzeug.routing import RequestRedirect
from werkzeug.routing import RoutingException
from werkzeug.routing import Rule
from werkzeug.serving import is_running_from_reloader
from werkzeug.wrappers import Response as BaseResponse

这段代码主要做了一些Python模块的导入。下面我会为你详细解释每一部分:

  1. from __future__ import annotations
    这是Python 3.7之后引入的特性,允许延迟对类型注释的解析,使得类型注释可以在循环引用等复杂场景中更加灵活地使用。

  2. import collections.abc as cabc
    collections.abc导入所有的抽象基类(例如Iterable, Mapping, Sequence等),并将它们命名为cabc以简化引用。

  3. import os, sys, typing as t
    导入常用的模块:os用于与操作系统交互,sys提供了与Python解释器相关的变量和函数,typing(这里重命名为t)提供了类型提示的支持。

  4. import weakref
    导入weakref模块,它允许创建对对象的弱引用,即不会增加对象的引用计数。

  5. from datetime import timedelta
    datetime模块导入timedelta类,它表示两个日期或时间之间的差。

  6. from inspect import iscoroutinefunction
    inspect模块导入iscoroutinefunction函数,用于检查一个对象是否是协程函数。

  7. from itertools import chain
    itertools模块导入chain函数,它可以将多个迭代器链接成一个。

  8. from types import TracebackType
    types模块导入TracebackType,它表示Python的异常回溯类型。

  9. from urllib.parse import quote as _url_quote
    urllib.parse模块导入quote函数,并将其重命名为_url_quote,用于URL编码。

  10. import click
    导入click模块,这是一个用于创建命令行接口的Python库。

  11. werkzeug导入多个模块和类

    • Headers:用于表示HTTP头信息的类。
    • ImmutableDict:不可变的字典类。
    • BadRequestKeyError, HTTPException, InternalServerError:这些都是werkzeug提供的异常类。
    • BuildError, MapAdapter, RequestRedirect, RoutingException, Rule:这些都是与路由相关的类和异常。
    • BaseResponse:表示HTTP响应的基类。
    • is_running_from_reloader:一个函数,用于检查应用是否正在从Werkzeug的reloader中运行。

总的来说,这段代码主要导入了一些Python的标准库模块和一些第三方库(如clickwerkzeug),为接下来的代码提供了所需的功能和工具。特别是werkzeug库,它是Python Web开发中的一个重要库,提供了许多与Web请求、响应和路由相关的功能。

from . import cli
from . import typing as ft
from .ctx import AppContext
from .ctx import RequestContext
from .globals import _cv_app
from .globals import _cv_request
from .globals import current_app
from .globals import g
from .globals import request
from .globals import request_ctx
from .globals import session
from .helpers import get_debug_flag
from .helpers import get_flashed_messages
from .helpers import get_load_dotenv
from .helpers import send_from_directory
from .sansio.app import App
from .sansio.scaffold import _sentinel
from .sessions import SecureCookieSessionInterface
from .sessions import SessionInterface
from .signals import appcontext_tearing_down
from .signals import got_request_exception
from .signals import request_finished
from .signals import request_started
from .signals import request_tearing_down
from .templating import Environment
from .wrappers import Request
from .wrappers import Response

if t.TYPE_CHECKING:  # pragma: no cover
    from _typeshed.wsgi import StartResponse
    from _typeshed.wsgi import WSGIEnvironment

    from .testing import FlaskClient
    from .testing import FlaskCliRunner

这段Python代码主要做了以下几件事情:

  1. 模块导入
    • 它从当前包(. 表示当前包)中导入了一系列的模块、类和变量。这些模块、类和变量大多与Web框架的功能相关,涵盖了命令行接口(cli)、类型注解(typing)、上下文管理(ctx)、全局变量(globals)、帮助函数(helpers)、应用构建(sansio.app)、会话管理(sessions)、信号(signals)、模板环境(templating)以及请求和响应的封装(wrappers)。
  2. 类型注解与类型检查
    • if t.TYPE_CHECKING: 这段代码检查一个常量 TYPE_CHECKING 是否为真。这通常用于在类型检查期间导入额外的类型注解,但在运行时不会执行这部分代码,因为类型注解在运行时是不需要的。这是一种优化手段,可以减少运行时的开销。
    • TYPE_CHECKING 为真的条件下,它导入了 StartResponseWSGIEnvironment,这两个都是WSGI(Web服务器网关接口)规范中定义的类型,用于Web服务器和Web应用之间的通信。
    • 同时,它还导入了 FlaskClientFlaskCliRunner,这两个可能是用于测试Flask应用的工具类。
  3. typing导入的区别
    • 代码中引用的 ttyping 的一个别名,通常在代码的开始部分会有 import typing as t 这样的导入语句。
    • 类似地,timedelta_make_timedelta 函数的参数类型注解中被使用,它通常来自 datetime 模块,即 from datetime import timedelta
    • 代码中引用的 ft 也是 typing (当前目录下的)的一个别名,前面的导入命令:from . import typing as ft

综上所述,这段代码主要是用于导入和设置与Web开发(特别是Flask框架)相关的各种模块、类和变量。同时,它也利用了Python的类型注解和类型检查功能来增强代码的可读性和健壮性。

T_shell_context_processor = t.TypeVar(
    "T_shell_context_processor", bound=ft.ShellContextProcessorCallable
)
T_teardown = t.TypeVar("T_teardown", bound=ft.TeardownCallable)
T_template_filter = t.TypeVar("T_template_filter", bound=ft.TemplateFilterCallable)
T_template_global = t.TypeVar("T_template_global", bound=ft.TemplateGlobalCallable)
T_template_test = t.TypeVar("T_template_test", bound=ft.TemplateTestCallable)


def _make_timedelta(value: timedelta | int | None) -> timedelta | None:
    if value is None or isinstance(value, timedelta):
        return value

    return timedelta(seconds=value)

在这段代码中,主要完成了以下事情:

  1. 定义类型变量(Type Variables)
    • 使用 t.TypeVar 创建了几个类型变量 T_shell_context_processorT_teardownT_template_filterT_template_globalT_template_test。这些类型变量都绑定到特定的类型上,即 ft.ShellContextProcessorCallableft.TeardownCallableft.TemplateFilterCallableft.TemplateGlobalCallableft.TemplateTestCallable。这允许在后续的泛型函数或类中使用这些类型变量,来指定函数或类参数的类型,同时保持一定的灵活性。
  2. 定义 _make_timedelta 函数
    • 这个函数接收一个参数 value,其类型可以是 timedeltaintNone
    • 函数首先检查 value 是否为 None 或者已经是一个 timedelta 对象。如果是,则直接返回 value
    • 如果 value 是一个整数,函数会创建一个新的 timedelta 对象,其秒数等于 value,并返回这个新创建的 timedelta 对象。
    • 这个函数的作用是将整数秒数转换为 timedelta 对象,或者如果输入已经是 timedeltaNone,则直接返回。这在处理时间间隔时非常有用,尤其是当时间间隔可能以不同的形式(整数秒数或 timedelta 对象)提供时。
class Flask(App):

      default_config = ImmutableDict(

    {
        "DEBUG": None,
        "TESTING": False,
        "PROPAGATE_EXCEPTIONS": None,
        "SECRET_KEY": None,
        "PERMANENT_SESSION_LIFETIME": timedelta(days=31),
        "USE_X_SENDFILE": False,
        "SERVER_NAME": None,
        "APPLICATION_ROOT": "/",
        "SESSION_COOKIE_NAME": "session",
        "SESSION_COOKIE_DOMAIN": None,
        "SESSION_COOKIE_PATH": None,
        "SESSION_COOKIE_HTTPONLY": True,
        "SESSION_COOKIE_SECURE": False,
        "SESSION_COOKIE_SAMESITE": None,
        "SESSION_REFRESH_EACH_REQUEST": True,
        "MAX_CONTENT_LENGTH": None,
        "SEND_FILE_MAX_AGE_DEFAULT": None,
        "TRAP_BAD_REQUEST_ERRORS": None,
        "TRAP_HTTP_EXCEPTIONS": False,
        "EXPLAIN_TEMPLATE_LOADING": False,
        "PREFERRED_URL_SCHEME": "http",
        "TEMPLATES_AUTO_RELOAD": None,
        "MAX_COOKIE_SIZE": 4093,
    }
)

#: The class that is used for request objects.  See :class:`~flask.Request`
#: for more information.
request_class: type[Request] = Request

#: The class that is used for response objects.  See
#: :class:`~flask.Response` for more information.
response_class: type[Response] = Response

#: the session interface to use.  By default an instance of
#: :class:`~flask.sessions.SecureCookieSessionInterface` is used here.
#:
#: .. versionadded:: 0.8
session_interface: SessionInterface = SecureCookieSessionInterface()

这段代码主要做了以下几件事情:

  1. 设置默认配置
    default_config 是一个 ImmutableDict 对象(一个不可变的字典),用于存储Flask应用的默认配置。这个字典包含了一系列的键值对,其中键是配置选项的名称,值是这些选项的默认值。这些配置选项涵盖了诸如调试模式、测试模式、密钥、会话生命周期、cookie设置等各种应用级别的设置。

  2. 设置请求和响应的类
    request_class 是一个类型注解,指定用于请求对象的类。这里它被设置为 Request 类,该类是Flask框架中用于表示HTTP请求的对象。

    response_class 同样是一个类型注解,指定用于响应对象的类。这里它被设置为 Response 类,该类用于表示HTTP响应。

  3. 设置会话接口
    session_interface 是一个类型注解,用于指定会话的接口。默认情况下,它被设置为 SecureCookieSessionInterface 的一个实例。SecureCookieSessionInterface 是Flask框架中用于管理基于cookie的会话的类。这意味着,默认情况下,Flask使用cookie来存储用户的会话信息。

  4. ImmutableDict 是一个不可变的字典,这意味着一旦创建,其内容就不能被修改。这有助于确保Flask的默认配置不会被意外地更改,从而提高了代码的健壮性和可维护性。

def __init__(
    self,
    import_name: str,
    static_url_path: str | None = None,
    static_folder: str | os.PathLike[str] | None = "static",
    static_host: str | None = None,
    host_matching: bool = False,
    subdomain_matching: bool = False,
    template_folder: str | os.PathLike[str] | None = "templates",
    instance_path: str | None = None,
    instance_relative_config: bool = False,
    root_path: str | None = None,
):
    super().__init__(
        import_name=import_name,
        static_url_path=static_url_path,
        static_folder=static_folder,
        static_host=static_host,
        host_matching=host_matching,
        subdomain_matching=subdomain_matching,
        template_folder=template_folder,
        instance_path=instance_path,
        instance_relative_config=instance_relative_config,
        root_path=root_path,
    )

    #: The Click command group for registering CLI commands for this
    #: object. The commands are available from the ``flask`` command
    #: once the application has been discovered and blueprints have
    #: been registered.
    self.cli = cli.AppGroup()

    # Set the name of the Click group in case someone wants to add
    # the app's commands to another CLI tool.
    self.cli.name = self.name

    # Add a static route using the provided static_url_path, static_host,
    # and static_folder if there is a configured static_folder.
    # Note we do this without checking if static_folder exists.
    # For one, it might be created while the server is running (e.g. during
    # development). Also, Google App Engine stores static files somewhere
    if self.has_static_folder:
        assert (
            bool(static_host) == host_matching
        ), "Invalid static_host/host_matching combination"
        # Use a weakref to avoid creating a reference cycle between the app
        # and the view function (see #3761).
        self_ref = weakref.ref(self)
        self.add_url_rule(
            f"{self.static_url_path}/<path:filename>",
            endpoint="static",
            host=static_host,
            view_func=lambda **kw: self_ref().send_static_file(**kw),  # type: ignore # noqa: B950
        )

这段代码是Flask应用类的初始化方法 __init__ 的实现。这个初始化方法接受多个参数,用于配置Flask应用的各种属性。以下是对这段代码的详细解释:

  • import_name: 通常是一个Python模块或包的名称,用于找到应用的根路径。
  • static_url_path: 静态文件的URL路径。
  • static_folder: 静态文件的文件夹名称或路径。
  • static_host: 静态文件的主机名。
  • host_matching: 是否启用主机名匹配。
  • subdomain_matching: 是否启用子域名匹配。
  • template_folder: 模板文件的文件夹名称或路径。
  • instance_path: 应用实例文件夹的路径。
  • instance_relative_config: 是否从实例文件夹加载配置。
  • root_path: 应用的根路径。
  1. 调用父类初始化方法: 通过 super().__init__() 调用父类的初始化方法,传递相同的参数,以初始化父类中的相关属性。
  2. 创建CLI命令组: 创建一个Click命令组 self.cli,用于注册此对象的CLI命令。这些命令在发现应用并注册蓝图后,可从 flask 命令中使用。
  3. 设置CLI命令组的名称: 将 self.cli 的名称设置为应用的名称 self.name
  4. 检查静态文件夹和主机名匹配: 如果应用有静态文件夹 (self.has_static_folderTrue),则检查 static_hosthost_matching 的组合是否有效。如果不有效,将触发断言错误。
  5. 添加静态文件URL规则: 如果应用有静态文件夹,并且提供了 static_host,则添加一个URL规则,用于处理静态文件的请求。这个规则使用了一个弱引用 self_ref 来避免在视图函数和应用之间创建引用循环。弱引用允许在不增加引用计数的情况下引用对象,这有助于垃圾回收。
  6. 代码中使用了类型注解(如 str | None),这表示该参数可以是字符串类型或 None
  7. self_ref().send_static_file(**kw) 中的 self_ref() 是一个弱引用到当前应用的实例。由于 self_ref 是一个弱引用,它不会阻止应用实例被垃圾回收。
  8. # type: ignore# noqa: B950 是类型检查器的指令,用于忽略某些类型相关的警告或错误。
  9. 这段代码是Flask应用初始化的部分,它处理应用的各种配置,并设置一些与静态文件和CLI命令相关的属性。代码使用了类型注解和弱引用来提高代码的健壮性和可读性。

def get_send_file_max_age(self, filename: str | None) -> int | None:
    """Used by :func:`send_file` to determine the ``max_age`` cache
    value for a given file path if it wasn't passed.

    By default, this returns :data:`SEND_FILE_MAX_AGE_DEFAULT` from
    the configuration of :data:`~flask.current_app`. This defaults
    to ``None``, which tells the browser to use conditional requests
    instead of a timed cache, which is usually preferable.

    Note this is a duplicate of the same method in the Flask
    class.

    .. versionchanged:: 2.0
        The default configuration is ``None`` instead of 12 hours.

    .. versionadded:: 0.9
    """
    value = current_app.config["SEND_FILE_MAX_AGE_DEFAULT"]

    if value is None:
        return None

    if isinstance(value, timedelta):
        return int(value.total_seconds())

    return value  # type: ignore[no-any-return]

这段代码定义了一个名为 get_send_file_max_age 的方法,它用于确定当未传递 max_age 参数时,send_file 函数所使用的缓存值。这个方法对于给定的文件路径来说是必要的。

具体来说,这个方法的功能如下:

  1. 它首先尝试从 Flask 应用的配置中获取 SEND_FILE_MAX_AGE_DEFAULT 的值。这个值通常用于决定浏览器缓存文件的时间长度。
  2. 如果获取到的值是 None,则方法直接返回 None。这告诉浏览器使用条件请求而不是定时缓存,这通常是更可取的方式。
  3. 如果获取到的值是一个 timedelta 对象(表示一个时间差),则方法会将其转换为总秒数并返回。
  4. 如果获取到的值不是 None 也不是 timedelta 对象,则方法直接返回这个值。这里使用了一个类型忽略注释(# type: ignore[no-any-return]),可能是因为代码分析工具认为这个方法可能会返回 Any 类型,而实际上根据前面的逻辑,它应该只返回 intNone
  5. 这个方法非常有用,因为它允许你根据应用的配置动态地设置文件的缓存时间。

此外,这段文档字符串(docstring)还包含了一些版本信息:

  • 在 Flask 2.0 版本中,默认配置被更改为 None,而不是 12 小时。
  • 这个方法在 Flask 0.9 版本中被引入。
def send_static_file(self, filename: str) -> Response:
    """The view function used to serve files from
    :attr:`static_folder`. A route is automatically registered for
    this view at :attr:`static_url_path` if :attr:`static_folder` is
    set.

    Note this is a duplicate of the same method in the Flask
    class.

    .. versionadded:: 0.5

    """
    if not self.has_static_folder:
        raise RuntimeError("'static_folder' must be set to serve static_files.")

    # send_file only knows to call get_send_file_max_age on the app,
    # call it here so it works for blueprints too.
    max_age = self.get_send_file_max_age(filename)
    return send_from_directory(
        t.cast(str, self.static_folder), filename, max_age=max_age
    )

这个方法用于从 Flask 应用的 static_folder(静态文件夹)中发送文件。如果 static_folder 已经被设置,那么 Flask 会自动为这个视图在 static_url_path 注册一个路由。

方法首先检查 has_static_folder 属性是否为 True,如果不是,则抛出一个运行时错误,因为必须设置 static_folder 才能发送静态文件。

接着,它调用 get_send_file_max_age 方法来获取文件的 max_age 缓存值。这个值用于确定浏览器应该缓存这个文件多长时间。如果 max_ageNone,浏览器会使用条件请求而不是定时缓存。

最后,它使用 send_from_directory 函数从 static_folder 发送文件,并应用之前获取的 max_age 缓存值。

def open_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
    """Open a resource file relative to :attr:`root_path` for
    reading.

    For example, if the file ``schema.sql`` is next to the file
    ``app.py`` where the ``Flask`` app is defined, it can be opened
    with:

    .. code-block:: python

        with app.open_resource("schema.sql") as f:
            conn.executescript(f.read())

    :param resource: Path to the resource relative to
        :attr:`root_path`.
    :param mode: Open the file in this mode. Only reading is
        supported, valid values are "r" (or "rt") and "rb".

    Note this is a duplicate of the same method in the Flask
    class.

    """
    if mode not in {"r", "rt", "rb"}:
        raise ValueError("Resources can only be opened for reading.")

    return open(os.path.join(self.root_path, resource), mode)

这个方法用于打开 root_path 的资源文件以供读取。root_path 是 Flask 应用所在的文件系统的根目录。

方法首先检查 mode 参数是否合法,只接受 "r"、 "rt"和 "rb" 这三种读取模式。如果 mode 不是这三种之一,则抛出 ValueError

然后,它使用 os.path.join 来构造文件的完整路径,并使用 Python 的 open 函数打开这个文件。返回的文件对象可以用于读取文件内容。

以上两个方法都是 Flask 框架内部使用的,用于处理文件相关的操作。通常不会被直接调用,而是在 Flask 应用的路由处理函数中通过 Flask 实例来间接使用。这两个方法的实现确保了 Flask 应用能够正确地处理静态文件和其他资源文件。

def open_instance_resource(self, resource: str, mode: str = "rb") -> t.IO[t.AnyStr]:
    """Opens a resource from the application's instance folder
    (:attr:`instance_path`).  Otherwise works like
    :meth:`open_resource`.  Instance resources can also be opened for
    writing.

    :param resource: the name of the resource.  To access resources within
                     subfolders use forward slashes as separator.
    :param mode: resource file opening mode, default is 'rb'.
    """
    return open(os.path.join(self.instance_path, resource), mode)

 open_instance_resource 方法用于打开 Flask 应用实例文件夹(instance_path)中的资源文件。

  • resource:要打开的资源文件的名称。如果资源位于子文件夹中,可以使用正斜杠(/)作为分隔符来指定路径。
  • mode:打开资源文件的模式,默认为 'rb',即二进制读取模式。这意味着默认情况下,这个方法用于读取文件,但也可以指定其他模式(如 'w''a' 等)来写入或追加文件内容。

方法的实现细节如下:

  1. 使用 os.path.join 函数将 self.instance_path(实例文件夹的路径)和 resource(资源文件的名称或路径)拼接成一个完整的文件路径。
  2. 使用 Python 内置的 open 函数打开拼接得到的文件路径,并返回文件对象。文件的打开模式由 mode 参数指定。

这个方法用于在 Flask 应用中动态地读取或写入实例文件夹中的文件。实例文件夹是 Flask 应用中的一个特殊目录,用于存放那些不应该包含在版本控制中,但需要在运行时访问的文件,比如配置文件、日志文件或临时数据等。通过 open_instance_resource 方法,开发者可以方便地打开和操作这些文件,而无需担心文件路径的拼接和文件打开的具体细节。

def create_jinja_environment(self) -> Environment:
    """Create the Jinja environment based on :attr:`jinja_options`
    and the various Jinja-related methods of the app. Changing
    :attr:`jinja_options` after this will have no effect. Also adds
    Flask-related globals and filters to the environment.

    .. versionchanged:: 0.11
       ``Environment.auto_reload`` set in accordance with
       ``TEMPLATES_AUTO_RELOAD`` configuration option.

    .. versionadded:: 0.5
    """
    options = dict(self.jinja_options)

    if "autoescape" not in options:
        options["autoescape"] = self.select_jinja_autoescape

    if "auto_reload" not in options:
        auto_reload = self.config["TEMPLATES_AUTO_RELOAD"]

        if auto_reload is None:
            auto_reload = self.debug

        options["auto_reload"] = auto_reload

    rv = self.jinja_environment(self, **options)
    rv.globals.update(
        url_for=self.url_for,
        get_flashed_messages=get_flashed_messages,
        config=self.config,
        # request, session and g are normally added with the
        # context processor for efficiency reasons but for imported
        # templates we also want the proxies in there.
        request=request,
        session=session,
        g=g,
    )
    rv.policies["json.dumps_function"] = self.json.dumps
    return rv

方法create_jinja_environment用于创建 Jinja2 模板引擎的环境。Jinja2 是 Flask 使用的默认模板引擎,用于渲染 HTML 页面。

方法定义

  • def create_jinja_environment(self) -> Environment: 定义一个方法,名为 create_jinja_environment,该方法参数self,代表 Flask 类的实例,并返回一个 Jinja2 的 Environment 对象。

方法功能

  1. 初始化 Jinja2 配置
    • 方法首先通过 self.jinja_options 获取 Jinja2 的配置选项,并创建一个新的字典 options 来存储这些选项。
    • 如果 options 中没有包含 "autoescape" 配置项,则将其设置为 self.select_jinja_autoescape,用于自动转义 HTML 模板中的特定部分以防止 XSS 攻击。
    • 如果 options 中没有包含 "auto_reload" 配置项,方法会检查 Flask 应用的配置中是否设置了 "TEMPLATES_AUTO_RELOAD"。如果没有明确设置,它会根据应用的调试模式(self.debug)来决定是否启用模板的自动重载。
  2. 创建 Jinja2 环境
    • 调用 self.jinja_environment(self, **options) 来创建一个新的 Jinja2 环境对象。self.jinja_environment 是一个工厂函数,用于生成 Environment 对象。
  3. 添加全局变量和过滤器
    • 方法通过 rv.globals.update(...) 向 Jinja2 环境添加了一些全局变量和函数,这些变量和函数在模板渲染过程中可以直接使用。例如,url_for 用于生成 URL,get_flashed_messages 用于获取一次性消息(通常用于显示通知),config 是 Flask 的配置对象,request, session, g 是 Flask 提供的几个特殊对象,用于访问请求信息、会话数据和应用级别的全局变量。
  4. 设置 JSON 序列化函数
    • rv.policies["json.dumps_function"] = self.json.dumps 将 Flask 应用的 JSON 序列化函数设置为 Jinja2 环境的一个策略,这样在模板中渲染 JSON 数据时,会使用 Flask 的序列化函数。
  5. 返回 Jinja2 环境
    • 最后,方法返回创建的 Environment 对象,这个对象可以在 Flask 应用中用于渲染模板。

这个方法在 Flask 应用初始化时调用,以确保 Jinja2 模板引擎的环境正确配置,并准备好用于后续的模板渲染。这样,开发者就可以通过 Flask 提供的方法(如 render_template)来方便地渲染模板并生成 HTML 页面。

def create_url_adapter(self, request: Request | None) -> MapAdapter | None:
    """Creates a URL adapter for the given request. The URL adapter
    is created at a point where the request context is not yet set
    up so the request is passed explicitly.

    .. versionadded:: 0.6

    .. versionchanged:: 0.9
       This can now also be called without a request object when the
       URL adapter is created for the application context.

    .. versionchanged:: 1.0
        :data:`SERVER_NAME` no longer implicitly enables subdomain
        matching. Use :attr:`subdomain_matching` instead.
    """
    if request is not None:
        # If subdomain matching is disabled (the default), use the
        # default subdomain in all cases. This should be the default
        # in Werkzeug but it currently does not have that feature.
        if not self.subdomain_matching:
            subdomain = self.url_map.default_subdomain or None
        else:
            subdomain = None

        return self.url_map.bind_to_environ(
            request.environ,
            server_name=self.config["SERVER_NAME"],
            subdomain=subdomain,
        )
    # We need at the very least the server name to be set for this
    # to work.
    if self.config["SERVER_NAME"] is not None:
        return self.url_map.bind(
            self.config["SERVER_NAME"],
            script_name=self.config["APPLICATION_ROOT"],
            url_scheme=self.config["PREFERRED_URL_SCHEME"],
        )

    return None

方法create_url_adapter用于创建 URL 适配器。URL 适配器负责将 URL 路径映射到 Flask 应用中的视图函数。

方法定义

  • def create_url_adapter(self, request: Request | None) -> MapAdapter | None:
    • 该方法接受一个参数 request,它是一个 Request 对象或者 None
    • 方法返回一个 MapAdapter 对象或者 None

功能描述

  • 该方法根据给定的请求对象创建一个 URL 适配器。因为请求上下文在此时可能尚未设置,所以请求对象是显式传递的。
  • URL 适配器用于解析和构建 URL,是处理请求和生成响应时的重要组件。

方法实现

  1. 有请求对象的情况
    • 如果 subdomain_matching 被禁用(默认情况),则无论何种情况都使用 url_map 的默认子域名。
    • 如果 subdomain_matching 被启用,则子域名设为 None
    • 使用 request.environ(即请求的 WSGI 环境)和 self.config 中的配置信息(如 SERVER_NAMEsubdomain)来调用 self.url_map.bind_to_environ 方法,从而创建一个绑定到该请求的 URL 适配器,并返回。
  2. 没有请求对象的情况
    • 如果 SERVER_NAME 在 Flask 配置中未设置,那么无法创建 URL 适配器,方法返回 None
    • 如果 SERVER_NAME 已设置,那么使用 self.config 中的配置信息(如 SERVER_NAMEAPPLICATION_ROOTPREFERRED_URL_SCHEME)来调用 self.url_map.bind 方法,创建一个与应用上下文关联的 URL 适配器,并返回。

这个方法在 Flask 应用中非常重要,它使得应用能够根据当前的请求或应用上下文生成正确的 URL,这对于路由处理、URL 构建以及重定向等功能至关重要。

def raise_routing_exception(self, request: Request) -> t.NoReturn:
    """Intercept routing exceptions and possibly do something else.

    In debug mode, intercept a routing redirect and replace it with
    an error if the body will be discarded.

    With modern Werkzeug this shouldn't occur, since it now uses a
    308 status which tells the browser to resend the method and
    body.

    .. versionchanged:: 2.1
        Don't intercept 307 and 308 redirects.

    :meta private:
    :internal:
    """
    if (
        not self.debug
        or not isinstance(request.routing_exception, RequestRedirect)
        or request.routing_exception.code in {307, 308}
        or request.method in {"GET", "HEAD", "OPTIONS"}
    ):
        raise request.routing_exception  # type: ignore[misc]

    from .debughelpers import FormDataRoutingRedirect

    raise FormDataRoutingRedirect(request)

Flask 框架中的 raise_routing_exception 方法用于拦截和处理路由异常。

方法定义

  • def raise_routing_exception(self, request: Request) -> t.NoReturn:
    • 方法接受一个参数 request,类型为 Request
    • 方法返回类型为 t.NoReturn,表示这个方法不会正常返回,而是会抛出异常。

功能描述

  • 该方法用于拦截路由异常,并根据条件执行不同的操作。
  • 在调试模式下,如果路由异常是一个重定向(RequestRedirect),并且请求体(body)将被丢弃,则用一个错误替换这个重定向。
  • 在非调试模式下,或者异常不是重定向,或者重定向的状态码是 307 或 308,或者请求方法是 GET、HEAD、OPTIONS 之一时,直接抛出原始路由异常。

实现细节

  1. 条件判断
    • not self.debug:如果 Flask 应用不在调试模式下,直接抛出原始路由异常。
    • not isinstance(request.routing_exception, RequestRedirect):如果路由异常不是重定向,直接抛出原始路由异常。
    • request.routing_exception.code in {307, 308}:如果路由异常的重定向状态码是 307 或 308,直接抛出原始路由异常。
    • request.method in {"GET", "HEAD", "OPTIONS"}:如果请求方法是 GET、HEAD 或 OPTIONS,直接抛出原始路由异常。
  2. 异常处理
    • 如果上述条件都不满足,那么会导入一个名为 FormDataRoutingRedirect 的异常类(来自 debughelpers 模块),并用当前请求 request 作为参数,抛出一个 FormDataRoutingRedirect 异常。这个异常用于在调试模式下,当重定向可能导致请求体被丢弃时,提供一个更友好的错误信息或行为。

综上所述,raise_routing_exception 方法主要用于在 Flask 应用中拦截和处理路由异常,特别是在调试模式下,提供更友好的错误处理机制。

未完待续...

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值