ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

在 NocoBase 中通过插件扩展短信服务商:客户端注册配置表单与服务端实现 SMSProvider 完整指南

2026/9/14 22:43:07 拓冰建站 浏览量
在 NocoBase 中通过插件扩展短信服务商:客户端注册配置表单与服务端实现 SMSProvider 完整指南 在 NocoBase 中通过插件扩展短信服务商客户端注册配置表单与服务端实现 SMSProvider 完整指南【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase本文面向需要为 NocoBase 验证插件nocobase/plugin-verification接入自定义短信服务商的开发者讲解如何通过插件的形式在客户端注册服务商配置表单、在服务端实现并注册SMSProvider发送实现最终在“验证短信”功能中扩展出新的短信服务商类型。读完本文你将掌握短信 OTP 验证器的扩展原理、registerProvider的注册协议、配置表单的 Schema 写法以及阿里云、腾讯云内置实现与测试用例背后的调用链。背景验证插件与短信服务商扩展机制在 NocoBase 中短信验证码是内置的一种验证类型SMS OTP。其核心能力由nocobase/plugin-verification插件提供系统负责生成一次性动态验证码OTP、记录验证记录、限流与绑定手机号等完整流程而真正与短信服务商交互的“发送逻辑”被抽象成了可替换的 Provider 接口。因此开发者不需要改动验证插件的核心代码只需按照约定的协议注册新的服务商即可把阿里云、腾讯云之外的任何短信通道如国内其他云厂商、国际短信网关等接入到验证流程中。扩展短信服务商分为两个部分二者缺一不可客户端client注册服务商类型对应的配置表单AdminSettingsForm管理员在验证管理页面选择该服务商后会渲染出该表单供填写密钥、签名、模板等参数。服务端server实现SMSProvider抽象类的send方法与短信服务商 API 交互并通过registerProvider注册服务商类型。服务端注册时使用的name必须与客户端注册时使用的name完全一致二者通过该名称关联。客户端注册配置表单管理员在配置短信验证器时需要先选择短信服务商类型随后页面会出现与该服务商类型关联的配置表单。这个表单是由开发者自行在客户端注册的它决定了管理员能看到并填写哪些配置项如 AccessKey、签名、模板等。编写配置表单组件配置表单本质上是一个基于 Schema 的表单组件可以使用SchemaComponent与 Formily 的 Schema 语法来声明。以最常见的阿里云短信配置项为例表单通常包含accessKeyId、accessKeySecret等字段。字段标题通过{{t(..., { ns: NAMESPACE })}}模板引用国际化资源保证多语言环境下正常显示TextAreaWithGlobalScope是验证插件提供的输入组件支持在文本中引用全局变量作用域。import { Plugin, SchemaComponent } from nocobase/client; import PluginVerificationClient from nocobase/plugin-verification/client; import React from react; const CustomSMSProviderSettingsForm: React.FC () { return SchemaComponent schema{{ type: void, properties: { accessKeyId: { title: {{t(Access Key ID, { ns: ${NAMESPACE} })}}, type: string, x-decorator: FormItem, x-component: TextAreaWithGlobalScope, required: true, }, accessKeySecret: { title: {{t(Access Key Secret, { ns: ${NAMESPACE} })}}, type: string, x-decorator: FormItem, x-component: TextAreaWithGlobalScope, x-component-props: { password: true }, required: true, }, } }} / }各字段属性的含义属性说明title表单字段的显示标签可通过{{t(...)}}接入 i18ntype字段类型配置项通常为stringx-decorator表单装饰器使用FormItem以获得表单布局与校验能力x-component渲染组件验证插件中一般使用TextAreaWithGlobalScopex-component-props组件属性如{ password: true }表示以密码形式展示输入required是否必填如果你希望表单默认支持“引用全局变量”使用TextAreaWithGlobalScope即可该组件与验证插件内置的 阿里云配置表单、腾讯云配置表单 保持一致保证交互体验统一。在插件 load 中注册编写完表单组件后在自定义插件的客户端load()中获取验证插件的实例并通过smsOTPProviderManager.registerProvider(name, { components: { AdminSettingsForm } })进行注册class PluginCustomSMSProviderClient extends Plugin { async load() { const plugin this.app.pm.get(verification) as PluginVerificationClient; plugin.smsOTPProviderManager.registerProvider(custom-sms-provider-name, { components: { AdminSettingsForm: CustomSMSProviderSettingsForm, }, }); } }从源码看客户端SMSOTPProviderManager内部使用Registry维护服务商注册表registerProvider(type, options)将服务商名称映射到{ components: { AdminSettingsForm } }getProvider(type)则用于按名称取回见 provider-manager.ts。内置的阿里云、腾讯云服务商正是通过同一机制在 客户端入口 注册的PROVIDER_TYPE_SMS_ALIYUN、PROVIDER_TYPE_SMS_TENCENT。注意name即custom-sms-provider-name是服务商类型的唯一标识必须与服务端注册时使用的名称一致表单中声明的字段如accessKeyId会作为配置对象options的一部分在服务端实例化 Provider 时通过this.options读取。服务端实现发送接口验证插件已经对 OTP 的生成、记录、校验与限流等流程做了完整封装开发者只需实现“与短信服务商交互的发送逻辑”。继承 SMSProvider 抽象类服务端SMSProvider是发送实现的基类定义于 providers/index.tsexport class SMSProvider { constructor(protected options: any) {} async send(receiver: string, data: { [key: string]: any }): Promiseany {} }constructor(options)options即管理员在客户端配置表单中填写的配置对象服务端会先经过全局变量模板渲染见下文。send(receiver, data)接收手机号receiver与验证码数据data至少包含code字段实现中应调用短信服务商 SDK 将验证码发送到目标手机号。自定义服务商的发送实现如下class CustomSMSProvider extends SMSProvider { constructor(options) { super(options); // options 为客户端的配置对象 const options this.options; // ... 初始化短信 SDK 客户端 } async send(phoneNumber: string, data: { code: string }) { // ... 调用短信服务商 API 发送验证码 } }参考内置实现阿里云与腾讯云为了让发送实现更贴合真实场景可以对照验证插件内置的两个 Provider阿里云sms-aliyun.ts构造函数中从this.options读取accessKeyId、accessKeySecret、endpoint初始化alicloud/dysmsapi20170525客户端send中读取sign短信签名与template模板 code将data序列化为templateParam调用发送接口并根据返回码将错误归一化为InvalidReceiver、RateLimit、SendSMSFailed等错误名。腾讯云sms-tencent.ts构造函数读取secretId、secretKey、region、endpoint初始化tencentcloud-sdk-nodejs-sms客户端send中读取SignName、TemplateId、SmsSdkAppId将data.code放入TemplateParamSet调用SendSms同样将频控类错误映射为RateLimit、号码错误映射为InvalidReceiver。内置实现的“错误归一化”约定值得在自定义 Provider 中延续验证插件会根据错误名决定如何提示用户例如号码非法、触发频控因此建议在send中把服务商返回的各类错误映射为一致的错误名而不是直接抛出服务商原始错误。关于 options 与全局变量渲染在 SMSOTPVerification.getProvider 中可以看到服务商实例的创建过程是const options this.ctx.app.environment.renderJsonTemplate(settings); return new Provider(options);即管理员填写的settings配置会先经过应用环境的全局变量模板渲染renderJsonTemplate再作为options传入 Provider 构造函数。这意味着你可以在配置表单中通过全局变量引用环境级敏感信息如密钥避免把密钥明文存入库表——这也是内置表单使用TextAreaWithGlobalScope的原因。注册验证类型发送接口实现好后在自定义插件的服务端load()中获取验证插件实例通过smsOTPProviderManager.registerProvider(name, { title, provider })注册服务商类型import { Plugin } from nocobase/server; import PluginVerificationServer from nocobase/plugin-verification; import { tval } from nocobase/utils; class PluginCustomSMSProviderServer extends Plugin { async load() { const plugin this.app.pm.get(verification) as PluginVerificationServer; // name 需要和客户端对应 plugin.smsOTPProviderManager.registerProvider(custom-sms-provider-name, { title: tval(Custom SMS provider, { ns: namespace }), provider: CustomSMSProvider, }); } }registerProvider的注册协议在服务端定义如下sms/index.tstype SMSProviderOptions { title: string; provider: typeof SMSProvider; }; registerProvider(type: string, options: SMSProviderOptions) { this.providers.register(type, options); }title服务商在管理界面中展示的名称可通过tval接入国际化provider继承自SMSProvider的发送实现类nametype与客户端注册名称一致的服务商标识。内置的阿里云、腾讯云正是以相同方式在 服务端 Plugin.ts 的load()中完成注册。注册完成后验证管理页面的服务商下拉列表即可通过smsOTPProviders资源见 sms-otp-providers.ts读取到新服务商。扩展机制原理注册表与运行时查找综合客户端与服务端的实现整个扩展机制可以概括为“两套注册表 按名称运行时查找”客户端注册表SMSOTPProviderManager.providersRegistry保存name - { components: { AdminSettingsForm } }用于验证管理页面渲染配置表单。服务端注册表SMSOTPProviderManager.providersRegistry保存name - { title, provider }用于发送验证码时实例化 Provider。运行时查找当管理员为某个验证器选择了服务商类型providerType后SMSOTPVerification.getProvider()会通过plugin.smsOTPProviderManager.providers.get(providerType)找到注册的 Provider 类并实例化若未找到或未配置则返回null对应“未选择服务商”的降级处理。这条链路意味着扩展一个新的短信服务商不需要修改验证插件、不需要新增表结构、不需要改动验证流程完全依赖注册表的动态性这也是插件化扩展的典型范式。验证扩展效果参考测试用例验证插件提供了针对 SMS OTP 流程的完整测试用例verify.test.ts可以作为扩展后自测的参照。测试中的做法与你扩展自定义服务商的方式完全一致class MockSMSProvider extends SMSProvider { async send() { return true; } } const plugin app.pm.get(verification) as PluginVerficationServer; plugin.smsOTPProviderManager.registerProvider(mock, { title: Mock, provider: MockSMSProvider, });随后测试会通过仓库创建verifiers记录verificationType: sms-otp、options.provider: mock再调用smsOTP资源创建验证码记录。测试覆盖的关键行为包括发送验证码后生成otpRecords记录action、receiver、verifierName等字段发送限流同一手机号短时间内重复请求第二次返回429校验失败错误验证码返回400与提示Verification code is invalid校验成功使用正确的code校验后记录状态从0更新为1过期校验验证码过期后即使 code 正确也返回400失败频控连续 6 次错误后触发429限流。这些用例验证了“注册 Provider → 发送 → 校验”的完整闭环也间接证明了只要按registerProvider协议注册自定义服务商即可无缝接入现有 OTP 流程并自动获得限流、记录、过期等内置能力。注意事项与最佳实践名称一致性客户端与服务端注册的name必须完全一致否则管理界面无法渲染对应配置表单或发送时无法找到 Provider。表单字段与服务端配置对齐客户端表单声明的字段名如accessKeyId就是服务端this.options的键名务必与SMSProvider实现中的读取保持一致。敏感信息处理建议密钥类字段使用TextAreaWithGlobalScope并配合全局变量渲染renderJsonTemplate避免明文入库同时可在表单中使用x-component-props: { password: true }遮挡输入。错误归一化在send中把服务商返回的错误统一映射为InvalidReceiver号码非法、RateLimit频控等约定错误名便于验证插件给出准确的用户提示与限流处理。模板参数在短信服务商后台配置模板时需为验证码预留参数例如阿里云模板您的验证码为${code}、腾讯云模板您的验证码为{1}参见 验证短信 文档确保send的data.code能正确渲染。插件加载时机注册动作放在自定义插件客户端的load()与服务端的load()中app.pm.get(verification)要求验证插件已启用且加载顺序在自定义插件之前。小结通过客户端registerProvider注册配置表单、服务端继承SMSProvider并registerProvider注册发送实现即可在 NocoBase 中无缝扩展任意短信服务商。整个扩展完全基于验证插件开放的注册表机制开发者无需改动插件核心代码就能让短信验证码能力覆盖阿里云、腾讯云之外的更多通道并自动获得 OTP 生成、限流、过期、记录等内置能力。如需进一步了解验证插件的整体使用方式添加短信验证器、管理员配置、用户绑定/解绑流程可继续阅读 验证短信 及其姊妹篇 扩展验证类型、API 参考。【免费下载链接】nocobaseNocoBase is an open-source AI no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考