FEATURED · 精选文章

Bitwarden Server SeederApi:面向开发与测试环境的测试数据动态播种 REST API 完全指南

发布时间 / 2026/9/13 18:03:45
来源 / 创域科博编辑部
栏目 / 资讯中心
Bitwarden Server SeederApi:面向开发与测试环境的测试数据动态播种 REST API 完全指南 Bitwarden Server SeederApi面向开发与测试环境的测试数据动态播种 REST API 完全指南【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server本文基于 Bitwarden server 仓库中 util/SeederApi/README.md 展开系统讲解 SeederApi 这一测试数据播种服务的架构组成、全部 REST 端点的用法与参数细节、Play ID 追踪与清理机制、共享分布式缓存配置并结合util/SeederApi与src/SharedWeb中的实际源码剖析场景Scene/查询Query执行、定时清理 Job 与实体级联删除的底层实现帮助你从零搭建可用的集成测试数据环境并安全地完成数据回收。一、SeederApi 是什么SeederApi 是一个用于在开发与测试阶段向 Bitwarden 数据库动态播种seed和查询测试数据的 Web API。它提供 HTTP 端点来执行 Seeder 项目中定义的场景Scene与查询Query通过 RESTful 接口实现测试数据的自动化生成与获取。它最典型的使用场景包括集成测试为测试套件快速准备确定性的用户、组织数据本地开发工作流用一条curl命令即可造出带订阅、账单网关身份的用户或组织自动化测试环境结合 Play ID 机制实现播种—使用—清理的完整闭环。1.1 组件架构README 将 SeederApi 描述为三个主要组件Jobs 为第四部分Controllers—— 用于播种、查询和管理测试数据的 HTTP 端点Services—— 场景与查询执行的业务逻辑Models—— API 通信的请求/响应模型Jobs—— 通过JobsHostedService运行的定时任务。关键组件及其职责均对应仓库真实代码组件路由/职责源码位置SeedController/seed创建与销毁播种数据SeedController.csQueryController/query对已有数据执行只读查询QueryController.csInfoController健康检查与版本信息InfoController.csSceneService / SceneExecutor场景执行与清理带 Play ID 追踪SceneExecutor.csQueryService / QueryExecutor只读查询执行QueryExecutor.csCleanup Job每 15 分钟删除过期 play 数据DeleteOldPlayDataJob.cs从源码结构看InfoController 实际暴露GET /alive与GET /now两个匿名端点均返回当前 UTC 时间而 Seed/Query 两个控制器均标注了[Authorize]配合 Utilities 下的BasicAuthenticationHandler完成身份校验。二、启动 API 与核心工作流2.1 启动方式进入项目目录后直接运行cd util/SeederApi dotnet runAPI 将启动在配置的端口上通常为http://localhost:5000。2.2 播种数据POST /seed向/seed发送 POST 请求携带场景模板名与可选参数。必须在请求头中携带X-Play-Id以便后续按 Play ID 清理数据。两个硬性约束password参数必须是至少 8 个字符的主密码mock 用户账号的登录密码邮箱应始终使用顶层域为example.com的地址RFC 2606 保证该域名不可解析避免测试数据泄漏到真实邮箱。curl -X POST http://localhost:5000/seed \ -H Content-Type: application/json \ -H X-Play-Id: test-run-123 \ -d { template: SingleUserScene, arguments: { email: testexample.com, password: REPLACE_ME } }响应示例{ mangleMap: { testexample.com: 1854b016testexample.com, 42bcf05d-7ad0-4e27-8b53-b3b700acc664: 42bcf05d-7ad0-4e27-8b53-b3b700acc664 }, result: null }其中result是场景返回的数据mangleMap包含启用 ID mangle 时的 ID 映射如友好标识 → 实际数据库 ID。请求体与响应体在源码中分别对应 SeedRequestModel.csTemplate为必填、Arguments为可选的JsonElement与 SeedResponseModel.csMangleMapResult。后续使用X-Play-Id请求头的值即可销毁这批播种数据。执行链路源码级SeedController.SeedAsync调用 SceneExecutor.ExecuteAsync通过keyed DIserviceProvider.GetKeyedServiceIScene(templateName)按模板名查找场景找不到则抛SceneNotFoundExceptionHTTP 404通过scene.GetRequestType()得到场景的请求模型类型用JsonSerializer将arguments反序列化为该类型——因此arguments的字段名、类型必须与该场景的 C# 请求模型匹配枚举字段须以数值发送若未传arguments则用Activator.CreateInstance创建默认请求模型调用scene.SeedAsync(requestModel)将SceneResultT包装为{ mangleMap, result }返回未知异常统一包装为SceneExecutionExceptionHTTP 400脱敏底层细节。QueryExecutor 对IQuery采用完全相同的keyed 服务 类型化反序列化模式只差异在调用query.Execute(requestModel)且直接返回原始结果对象。2.3 播种参数化组织SingleOrganizationSceneSingleOrganizationScene按选定套餐plan播种一个组织并把一个已存在的用户链接为已确认的 owner——即先用SingleUserScene播种 owner再把其userId作为ownerUserId传入。除planType与seats外请求还接受overrides可选的能力/集合管理标志叠加在套餐默认值之上。任何未设置的标志保留套餐默认值。这包括useSecretsManager要播种一个关闭 Secrets Manager 的 Enterprise 组织需同时发送overrides.useSecretsManager: false与enableSecretsManager: false。若同时发送enableSecretsManager: true和overrides.useSecretsManager: falseSecrets Manager 保持开启。座位配置smSeats/smServiceAccounts仅在enableSecretsManager: true时生效。gateway、gatewayCustomerId、gatewaySubscriptionId—— 计费网关身份使播种出的组织看起来像真实已计费的组织。枚举字段planType、gateway必须以数值形式发送。下例中planType: 0即Freegateway: 0即Stripecurl -X POST http://localhost:5000/seed \ -H Content-Type: application/json \ -H X-Play-Id: test-run-123 \ -d { template: SingleOrganizationScene, arguments: { ownerUserId: 42bcf05d-7ad0-4e27-8b53-b3b700acc664, planType: 0, name: Acme, domain: acme.example, seats: 5, overrides: { useSso: true, useGroups: true }, gateway: 0, gatewayCustomerId: cus_123, gatewaySubscriptionId: sub_456 } }2.4 播种带计费网关身份的用户SingleUserSceneSingleUserScene播种独立用户。除email与password外请求还接受premium—— 为true时标记账户为 premium启用 1 GB 存储并设置 premium 到期时间gateway、gatewayCustomerId、gatewaySubscriptionId—— 计费网关身份使播种出的用户像一个真实链接了 Stripe或其他网关客户/订阅的云端 premium 用户。任何未设置的字段保持用户现有值不变。枚举字段gateway同样必须以数值发送gateway: 0即Stripecurl -X POST http://localhost:5000/seed \ -H Content-Type: application/json \ -H X-Play-Id: test-run-123 \ -d { template: SingleUserScene, arguments: { email: premiumexample.com, password: REPLACE_ME, premium: true, gateway: 0, gatewayCustomerId: cus_123, gatewaySubscriptionId: sub_456 } }从源码结构看premium 用户播种会附带写入用户 license 文件对应的清理逻辑也存在于销毁命令中见 第五节 的DeleteSeededUserLicenseFiles。2.5 查询数据POST /query向/query发送 POST 请求执行只读查询curl -X POST http://localhost:5000/query \ -H Content-Type: application/json \ -d { template: EmergencyAccessInviteQuery, arguments: { email: testexample.com } }响应示例[/accept-emergency?...]查询名未注册时返回 404执行失败返回 400见 QueryController 中QueryNotFoundException/QueryExecutionException的分支处理。三、销毁播种数据与级联删除实现SeederApi 提供三种删除端点全部位于 SeedController3.1 按 Play ID 删除使用播种时X-Play-Id请求头的同一值curl -X DELETE http://localhost:5000/seed/test-run-1233.2 按多个 Play ID 批量删除curl -X DELETE http://localhost:5000/seed/batch \ -H Content-Type: application/json \ -d [test-run-123, test-run-456]3.3 删除全部播种数据删除早于指定日期的、带有 Play ID 标记的播种数据。日期可选默认为请求时间往前推 1 天源码中为DateTime.UtcNow.AddDays(-1)且传入日期会先ToUniversalTime()统一为 UTCcurl -X DELETE http://localhost:5000/seed \ -H Content-Type: application/json \ -d 2026-03-23T10:45:47.0690009-10:003.4 Play 数据是临时的PlayData is ephemeral一个每 15 分钟运行的定时任务会删除标记了 Play ID 且超过 1 天的数据。任何希望长期保留的数据都不要打上 Play ID 标记。该定时任务在 JobsHostedService 中用 Quartz 注册Cron 触发器0 */15 * ? * *驱动DeleteOldPlayDataJob另有AliveJob按整点触发0 0 * * * ?Job 内部同样是取 1 天前的 Play ID 列表 → 调DestroyBatchScenesCommand批量销毁的逻辑与 3.3 的手动端点完全同构。3.5 级联删除的源码细节DestroySceneCommand 展示了实际的删除顺序值得集成测试使用者理解通过playItemRepository.GetByPlayIdAsync(playId)查出该 Play ID 关联的全部PlayItem提取 distinct 的userIds、organizationIds、providerIds先删 ProviderProviderUser/ProviderOrganization的关联行不会随 Provider 级联删除FK_ProviderUser_User、FK_ProviderOrganization_Organization必须先把 provider 关联行清掉再删 User先于 Organization 以满足外键约束随后DeleteSeededUserLicenseFiles尽力删除{LicenseDirectory}/user/{userId}.json形式的 license 文件文件错误仅记录日志、不中断数据库清理最后删 Organization逐条捕获异常并聚合为AggregateException最终包装成SceneExecutionException抛出PlayItem条目依赖数据库删除级联自动移除。批量删除接口DELETE /seed/batch与全量删除则通过DestroyBatchScenesCommand执行异常同样以聚合形式回传每个 Play ID 的失败明细HTTP 400 响应体中包含Details数组。四、Play ID 追踪机制当请求携带 Play ID 时User、Organization 等特定实体在创建时会被追踪。这使得实体在用完后可依据 Play ID 被删除。4.1 X-Play-Id 请求头重要所有 seed 请求都应携带X-Play-Id请求头-H X-Play-Id: your-unique-identifierPlay ID 可以是任何能唯一标识你的测试运行或会话的字符串。4.2 工作机制当 GlobalSettings 中启用TestPlayIdTrackingEnabled时PlayIdMiddleware 会自动完成从入站请求中提取x-play-id请求头为该请求作用域设置PlayIdService中的 Play ID追踪请求期间创建的所有实体用户、组织等将它们关联到PlayItem表中的 Play ID启用通过删除端点进行的完整清理。源码中还补充了两个 README 未展开的防御性细节见 PlayIdMiddleware.cs请求头值为空白时直接返回 400x-play-id header cannot be empty or whitespace长度上限为256 字符超出同样返回 400。SeederApi 自身在 appsettings.json 中已将globalSettings.testPlayIdTrackingEnabled设为true即该服务默认开启 Play ID 追踪。这一追踪机制对任何携带X-Play-Id请求头的 API 请求都生效不限于 SeederApi 端点。也就是说你可以追踪以下途径创建的实体场景执行—— 通过/seed端点播种的数据常规 API 操作—— 用户注册、创建组织、邀请成员等集成测试—— 测试执行期间对 Bitwarden API 的任意 HTTP 请求。反之不带X-Play-Id请求头的实体不会被追踪也无法通过删除端点清理。五、配置详解SeederApi 使用标准 Bitwarden 配置系统appsettings.json —— 基础配置含projectName: SeederApi与testPlayIdTrackingEnabled: trueappsettings.Development.json —— 开发环境覆盖dev/secrets.json—— 本地密钥数据库连接串等可参考 dev/secrets.json.exampleUser Secrets IDbitwarden-seeder-api。5.1 必需配置SeederApi 需要以下配置数据库连接Database Connection—— 指向 Bitwarden 数据库的连接串全局设置Global Settings—— 标准 BitwardenGlobalSettings配置分布式缓存Distributed Cache—— 需要读取持久化分布式缓存中验证码的查询例如UserEmailTokenCodeQuery它读取邮箱 2FA / 用户验证 OTP 码依赖此项。SeederApi 必须与生成该验证码的服务器Identity/Api共享同一个缓存后端。共享分布式缓存读取验证码的查询是在持久化 keyedIDistributedCache中查找验证码的因此只有当 SeederApi 与生成验证码的服务器指向同一后端时才能成功。在globalSettings.distributedCache下配置共享后端globalSettings: { distributedCache: { redis: { connectionString: same Redis connection as Identity/Api } } }Cosmos DBdistributedCache.cosmos.connectionString或自托管部署下的 SQL Serverdbo.Cache表是另外两个可选的共享后端。重要提示没有共享后端时SeederApi 会回退到进程本地in-memory缓存于是验证码读取类查询即使验证码在别处成功生成也会返回Found: false。后端选择逻辑可参见 ServiceCollectionExtensions.cs 中的AddDistributedCache。六、扩展新增场景与查询场景与查询定义在 Seeder 项目中。SeederApi 会自动发现并注册所有实现了场景与查询接口的类——从 SceneExecutor 与 QueryExecutor 的实现看这一自动发现落地为按类型名作为 key 的 keyed 依赖注入请求中的template值即为 DI 容器中注册的 key场景类名如SingleUserScene、SingleOrganizationScene。因此新增一个场景只需在 Seeder 中实现IScene/IQuery接口并注册对应 keyed 服务无需修改 SeederApi 的控制器或路由。七、安全注意事项警告SeederApi 仅面向开发与测试环境。切勿将该 API 部署到生产环境。它可以直接创建用户/组织、读取验证码类数据并批量删除标记数据端点能力本身即决定了它只能存在于非生产环境。八、快速对照端点速查表方法路径说明鉴权POST/seed按模板名播种场景响应含mangleMap与result[Authorize]DELETE/seed/{playId}删除指定 Play ID 的数据[Authorize]DELETE/seed/batch批量删除请求体为 Play ID 字符串数组[Authorize]DELETE/seed删除早于指定 UTC 时间默认 1 天前的全部播种数据[Authorize]POST/query执行只读查询[Authorize]GET/alive、/now健康检查返回当前 UTC 时间匿名适用前提小结运行 SeederApi 需要可访问的 Bitwarden 数据库、标准 GlobalSettings、以及若使用验证码读取类查询与 Identity/Api 共享的分布式缓存后端数据默认 1 天后被定时任务回收需长期保留的数据请勿打 Play ID 标记。【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻