
后端Web框架【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址https://gitcode.com/gh_mirrors/sa/sanic点击查看免费下载导读Blueprint蓝图是 Sanic 框架中用于子路由组织的核心对象它把路由、中间件、异常处理器、静态文件与监听器按业务域拆分到独立模块再以灵活、可插拔的方式注册到应用实例。本文以官方指南为主体结合仓库源码sanic/blueprints.py、sanic/app.py与测试用例tests/test_blueprints.py逐层讲解读完你将掌握 Blueprint 的创建与注册、复制、分组Blueprint.group、分组级中间件与版本化、copy()组合复用以及基于url_for()的端点 URL 生成能够把大型 Sanic 应用组织得清晰、可维护、可复用。概述什么是 BlueprintBlueprint 是用于在应用内部做子路由的对象。与直接把路由添加到应用实例不同Blueprint 提供了与Sanic()应用实例非常相似的 API大量同名装饰器定义好之后以灵活、可插拔的方式注册到应用中它允许开发者将路由、异常处理器、中间件及其他 Web 功能组织进独立、模块化的分组见 Blueprint 类源码 的 docstring尤其适合大型应用应用逻辑可以被拆分为若干个分组或职责区域互不干扰、便于维护从源码结构看Blueprint继承自BaseSanicsanic/blueprints.py并通过lazy(...)机制复用了route、middleware、exception、listener、signal、static等装饰器sanic/blueprints.py这就是它与应用实例 API 高度一致的底层原因。创建与注册创建 Blueprint首先创建 Blueprint 实例它的 API 与Sanic()应用实例非常相似很多装饰器可直接复用# ./my_blueprint.py from sanic.response import json from sanic import Blueprint bp Blueprint(my_blueprint) bp.route(/) async def bp_root(request): return json({my: blueprint})Blueprint构造函数支持以下参数见 sanic/blueprints.py参数类型默认值说明namestr必填Blueprint 的名称注册后用于端点命名与去重url_prefixstr \| NoneNone该 Blueprint 下所有路由的 URL 前缀构造时若以/结尾会被自动去除末尾斜杠hostlist[str] \| str \| NoneNone该 Blueprint 响应请求的域名或域名列表versionint \| str \| float \| NoneNone该 Blueprint 实现的 API 版本号strict_slashesbool \| NoneNone是否强制 URL 以斜杠结尾version_prefixstr/v版本号在 URL 中显示时的前缀注册到应用接着把 Blueprint 注册到应用实例上from sanic import Sanic from my_blueprint import bp app Sanic(__name__) app.blueprint(bp)app.blueprint()的完整签名如下sanic/app.pydef blueprint( self, blueprint: Blueprint | Iterable[Blueprint] | BlueprintGroup, *, url_prefix: str | None None, version: int | float | str | None None, strict_slashes: bool | None None, version_prefix: str | None None, name_prefix: str | None None, ) - None: ...也就是说除了 Blueprint 对象本身还可以在注册时传入url_prefix、version、strict_slashes、version_prefix、name_prefix等关键字参数对注册行为做一次性覆盖。注册期间Blueprint.register()会读取这些 options 并应用到其内部的_future_routes、_future_statics、_future_middleware、_future_exceptions、_future_listeners、_future_signals上见 sanic/blueprints.py。另外Blueprint 同样提供websocket()装饰器和add_websocket_route方法来实现 WebSocket 路由。v21.12 起注册前后均可继续添加从 v21.12 开始Blueprint 可以先注册、后添加内容。此前只有注册时已附着在 Blueprint 上的对象才会被加载进应用实例现在则可以这样写app.blueprint(bp) bp.route(/) async def bp_root(request): ...其底层机制是Blueprint.register()在注册后若发现自身已被注册self.registered为真会调用register_futures()将后续新增的 future路由、中间件、异常、监听器、信号同步注册到所有已关联的应用上sanic/blueprints.py。这一点在测试 tests/test_blueprints.py 中有基本验证。提示注册前访问bp.apps会抛出SanicException消息形如Blueprint test has not yet been registered to an app可用bp.registered属性判断是否已注册sanic/blueprints.py对应测试见 tests/test_blueprints.py。复制 Blueprintcopy()Blueprint 连同附着在其上的所有内容路由、WebSocket 路由、中间件、异常、监听器、静态文件都可以通过copy()方法复制到新实例。唯一必填参数是新的name同时你也可以用它覆盖旧 Blueprint 的任意属性值v1 Blueprint(Version1, version1) v1.route(/something) def something(request): pass v2 v1.copy(Version2, version2) app.blueprint(v1) app.blueprint(v2)此时可用路由为/v1/something /v2/somethingcopy()的完整参数见 sanic/blueprints.py参数默认值说明name必填新 Blueprint 的名称原名称会记录到copied_fromurl_prefix沿用旧值新实例的 URL 前缀version沿用旧值新实例的版本号version_prefix沿用旧值版本前缀allow_route_overwrite沿用旧值是否允许路由覆盖strict_slashes沿用旧值是否强制尾部斜杠with_registrationTrue是否把新实例同时注册到旧实例已关联的 Sanic 应用with_ctxFalse是否复制旧实例的ctx默认为否新实例获得空ctx从源码实现看copy()会深拷贝旧实例deepcopy、写入新名称与copied_from再按传入参数覆盖相应属性若with_registrationTrue且旧实例已注册还会自动对每个已关联应用调用app.blueprint(new_bp)让新 Blueprint 一并生效sanic/blueprints.py。复制时若旧实例已注册静态文件路由会抛出SanicException提示无法重复注册。相关断言见测试 tests/test_blueprints.py。该功能在 v21.9 中加入。Blueprint 分组Blueprint groupsBlueprint 也可以作为列表或元组的一部分注册注册器会递归遍历其中的任何子序列并逐个注册。官方提供了Blueprint.group()方法来简化这一过程可以模拟一套与前端目录结构对应的后端目录结构。考虑下面这个示例api/ ├──content/ │ ├──authors.py │ ├──static.py │ └──__init__.py ├──info.py └──__init__.py app.py第一个 Blueprint# api/content/authors.py from sanic import Blueprint authors Blueprint(content_authors, url_prefix/authors)第二个 Blueprint# api/content/static.py from sanic import Blueprint static Blueprint(content_static, url_prefix/static)Blueprint 分组# api/content/__init__.py from sanic import Blueprint from .static import static from .authors import authors content Blueprint.group(static, authors, url_prefix/content)第三个 Blueprint# api/info.py from sanic import Blueprint info Blueprint(info, url_prefix/info)另一个 Blueprint 分组# api/__init__.py from sanic import Blueprint from .content import content from .info import info api Blueprint.group(content, info, url_prefix/api)主服务器注册所有 Blueprint# app.py from sanic import Sanic from .api import api app Sanic(__name__) app.blueprint(api)最终挂载的 URL 前缀依次叠加为/api/content/authors、/api/content/static、/api/info。Blueprint.group()的完整签名sanic/blueprints.pystaticmethod def group( *blueprints: Blueprint | BlueprintGroup, url_prefix: str | None None, version: int | str | float | None None, strict_slashes: bool | None None, version_prefix: str /v, name_prefix: str | None , ) - BlueprintGroup: ...从实现看group()内部会chain递归展开嵌套的 list/tuple最终构造一个BlueprintGroup对象sanic/blueprints.py。BlueprintGroup实现了MutableSequence[Blueprint]sanic/blueprints.py因此支持索引、切片、append()、insert()、__len__等类似 list 的操作同时暴露url_prefix、version、strict_slashes、version_prefix、name_prefix等属性sanic/blueprints.py。尽管可以直接实例化BlueprintGroup官方推荐始终使用Blueprint.group()来创建。分组前缀与可组合性url_prefix与name_prefix如上所示创建分组时传入url_prefix可以为组内所有 Blueprint 统一扩展 URL 前缀非常适合搭建模拟目录结构的 API。此外group()还支持name_prefix参数用于让 Blueprint 可复用、可组合。当同一个 Blueprint 被应用到多个分组时name_prefix会让该 Blueprint 在每个分组中以唯一名称注册从而允许同一 Blueprint 多次注册且其每条路由都被赋予带唯一标识的名字。考虑这个例子生成的路由名如下TestApp.group-a_bp1.route1TestApp.group-a_bp2.route2TestApp.group-b_bp1.route1TestApp.group-b_bp2.route2bp1 Blueprint(bp1, url_prefix/bp1) bp2 Blueprint(bp2, url_prefix/bp2) bp1.add_route(lambda _: ..., /, nameroute1) bp2.add_route(lambda _: ..., /, nameroute2) group_a Blueprint.group( bp1, bp2, url_prefix/group-a, name_prefixgroup-a ) group_b Blueprint.group( bp1, bp2, url_prefix/group-b, name_prefixgroup-b ) app Sanic(TestApp) app.blueprint(group_a) app.blueprint(group_b)从源码看name_prefix在Blueprint.register()中会被拼接到路由名前面name f{opt_name_prefix}_{future.name}随后再交给app.generate_name()生成最终名称sanic/blueprints.py。同时app.blueprint()在递归注册分组时会合并各级url_prefix并将组级version、strict_slashes等向下继承sanic/app.py。name_prefix功能在 v23.6 中加入。中间件Blueprint 可以挂载仅作用于其自身端点的中间件bp.middleware async def print_on_request(request): print(I am a spy) bp.middleware(request) async def halt_request(request): return text(I halted the request) bp.middleware(response) async def halt_response(request, response): return text(I halted the response)类似地通过 Blueprint 分组可以把中间件应用到整组嵌套 Blueprintbp1 Blueprint(bp1, url_prefix/bp1) bp2 Blueprint(bp2, url_prefix/bp2) bp1.middleware(request) async def bp1_only_middleware(request): print(applied on Blueprint : bp1 Only) bp1.route(/) async def bp1_route(request): return text(bp1) bp2.route(/param) async def bp2_route(request, param): return text(param) group Blueprint.group(bp1, bp2) group.middleware(request) async def group_middleware(request): print(common middleware applied for both bp1 and bp2) # Register Blueprint group under the app app.blueprint(group)分组级中间件的实现方式是BlueprintGroup.middleware()把装饰器递归应用到组内每个 Blueprint 上嵌套分组同样会逐层展开并额外提供on_request/on_response便捷方法sanic/blueprints.py。注册时Blueprint.register()会把中间件与组内实际挂载的路由名关联起来从而保证中间件只作用于本组端点sanic/blueprints.py。异常处理与常规异常处理一样可以为 Blueprint 定义专属的异常处理器bp.exception(NotFound) def ignore_404s(request, exception): return text(Yep, I totally found the page: {}.format(request.url))分组同样支持整组异常处理BlueprintGroup.exception()会把异常处理器递归注册到组内每个 Blueprintsanic/blueprints.py。注册到应用时异常处理器同样只与组内路由名关联sanic/blueprints.py因此不会影响其他 Blueprint 或应用级路由。静态文件Blueprint 也可以拥有自己的静态文件处理器bp Blueprint(bp, url_prefix/bp) bp.static(/web/path, /folder/to/serve) bp.static(/web/path, /folder/to/server, nameuploads)随后可以用url_for()获取对应 URL路由相关细节可参考路由指南 print(app.url_for(static, namebp.uploads, filenamefile.txt)) /bp/web/path/file.txt注意这里的端点名遵循{blueprint_name}.{handler_name}规则静态资源命名空间是static配合 Blueprint 名称后即bp.uploads。测试中也有类似用法例如app.url_for(static, namestatic.testing)tests/test_blueprints.py。监听器ListenersBlueprint 同样可以实现监听器bp.listener(before_server_start) async def before_server_start(app, loop): ... bp.listener(after_server_stop) async def after_server_stop(app, loop): ...Blueprint.register()在注册阶段会把_future_listeners中每个监听器按事件名如before_server_start、after_server_stop应用到应用上sanic/blueprints.py因此 Blueprint 级监听器与应用级监听器在触发时机上保持一致。版本化Versioning正如版本化专题所讨论的Blueprint 可用于实现 Web API 的多个版本。version会被作为/v1、/v2等前缀拼接到路由前auth1 Blueprint(auth, url_prefix/auth, version1) auth2 Blueprint(auth, url_prefix/auth, version2)注册后/v1/auth与/v2/auth会分别指向两个 Blueprint从而实现每个 API 版本的子站点from auth_blueprints import auth1, auth2 app Sanic(__name__) app.blueprint(auth1) app.blueprint(auth2)也可以把多个 Blueprint 放入一个BlueprintGroup一次性为它们统一设置版本auth Blueprint(auth, url_prefix/auth) metrics Blueprint(metrics, url_prefix/metrics) group Blueprint.group(auth, metrics, versionv1) # 这将提供以下 URL 前缀的 API # /v1/auth/ 和 /v1/metrics关于版本化的补充要点源自版本化专题version可以是int、float或str如1、2.25、v1.1组级版本会被组内 Blueprint 继承除非 Blueprint 或路由自身显式覆盖优先级从高到低为路由级 Blueprint 级 Blueprint Group 级version_prefix默认是/v可在Blueprint构造、Blueprint.group()、app.blueprint()等多个位置定义更具体的定义覆盖更宽泛的定义一条路由的完整 URI 构成规则为version_prefix version url_prefix URI 定义与url_prefix类似version_prefix中也可以定义路径参数如version_prefix/foo:str/v。可组合性Composable一个Blueprint可以注册到多个分组而每个BlueprintGroup本身又可以被继续嵌套注册这带来了近乎无限的 Blueprint 组合可能。看下面的例子两个 handler 实际被挂载成了五5条不同的路由。app Sanic(__name__) blueprint_1 Blueprint(blueprint_1, url_prefix/bp1) blueprint_2 Blueprint(blueprint_2, url_prefix/bp2) group Blueprint.group( blueprint_1, blueprint_2, version1, version_prefix/api/v, url_prefix/grouped, strict_slashesTrue, ) primary Blueprint.group(group, url_prefix/primary) blueprint_1.route(/) def blueprint_1_default_route(request): return text(BP1_OK) blueprint_2.route(/) def blueprint_2_default_route(request): return text(BP2_OK) app.blueprint(group) app.blueprint(primary) app.blueprint(blueprint_1) # 挂载的路径 # /api/v1/grouped/bp1/ # /api/v1/grouped/bp2/ # /api/v1/primary/grouped/bp1 # /api/v1/primary/grouped/bp2 # /bp1这个例子同时展示了多个能力的叠加分组级version与version_prefix定制/api/v1、分组级url_prefix叠加/grouped、/primary/grouped、strict_slashes统一控制以及同一 Blueprintblueprint_1既被包含在分组中又单独注册。测试文件 tests/test_blueprint_group.py 与 tests/test_blueprints.py 中的test_blueprint_group_versioning、test_blueprint_group_strict_slashes、test_blueprint_registered_multiple_apps等用例分别验证了分组版本化、严格斜杠与多应用注册行为tests/test_blueprints.py、tests/test_blueprints.py、tests/test_blueprints.py。可组合性在 v21.6 中加入。生成 URLurl_for使用url_for()生成 URL 时端点名采用如下形式{blueprint_name}.{handler_name}即Blueprint 名称 . 处理器名称。例如前面name_prefix示例中生成的路由名TestApp.group-a_bp1.route1就是应用名 Blueprint 名 handler 名的完整拼接而静态资源的命名空间固定为static因此bp.uploads表示名为uploads的静态路由属于名为bp的 Blueprint。结合url_for()的通用能力如_anchor、_external、_host、_server、_scheme等特殊关键字参数见 sanic/app.py你可以为任何带name的 Blueprint 路由生成稳定的内部或外部 URL从而避免在代码中硬编码路径。小结何时使用 Blueprint综合官方指南与源码设计建议在以下场景使用 Blueprint应用按业务域拆分如users、orders、payments每个域一个或多个 Blueprint需要为同一组接口提供多版本version/version_prefix尤其适合用Blueprint.group()统一管理需要把同一套 Blueprint 注册到多个分组或应用利用name_prefix避免命名冲突需要为某组端点单独附加中间件、异常处理器、监听器与静态文件而不影响应用其他部分。进阶阅读完整的端点命名与 URL 生成规则见路由指南版本化的更多细节见版本化专题Blueprint 级异常处理与异常处理最佳实践一脉相承监听器机制可参考监听器指南。赞分享后端Web框架【免费下载链接】sanicAccelerate your web app development | Build fast. Run fast.项目地址https://gitcode.com/gh_mirrors/sa/sanic点击查看免费下载相关推荐Nuxeo未来路线图2024年最值得期待的5大功能升级Nuxeo未来路线图2024年最值得期待的5大功能升级 Nuxeo作为领先的内容管理平台2024年将迎来一系列重大功能升级为企业用户带来更强大的内容管理能后端CMS企业应用告别窗口混乱Windows 11文件资源管理器的标签页效率革命告别窗口混乱Windows 11文件资源管理器的标签页效率革命 你是否曾在处理多个项目文件时被满屏的文件资源管理器窗口搞得眼花缭乱每次打开新文件夹就多一个开发工具Lint代码质量PInvoke项目架构解析多目标框架支持与Windows Store兼容性PInvoke项目架构解析多目标框架支持与Windows Store兼容性 PInvoke是一个包含所有P/Invoke代码的类库让开发者无需重复导入系统调上一篇TiDB 非并行 HashAgg 内存溢写Spill to Disk设计与实现全解析下一篇Eel框架深度解析如何用Python轻松构建桌面GUI应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考