ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成

简介: ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成

在一般的系统中,往往也有短信模块的需求,如动态密码的登录,系统密码的找回,以及为了获取用户手机号码的短信确认等等,在ABP框架中,本身提供了对邮件、短信的基础支持,那么只需要根据自己的情况实现对应的接口即可。本篇随笔介绍ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成。

1、基于第三方阿里云短信的实现

阿里云短信的实现,GitHub上也有一些人实现了一些模块,我们只需要使用对应的模块,然后在Core模块中配置一下依赖即可。

我们一般在做某件事情的时候,先去看看别人是否已经做好了,使用它或者参考它来做事情是个不错的思路。

基于这个道理,我们可以在VS的Nuget包管理中查找一下基于ABP的阿里云短信,可以找到一个合适的进行参考。

这个阿里云的ABP实现适合我们当前的ABP框架版本,因此使用它即可,因此安装引入对应的类库在Core项目中。

 

在网站https://github.com/tangyanglai/Sms.Core 我们看到它的使用过程,引入后在项目中启动模块依赖中添加对应的代码即可。

 

[DependsOn(typeof(AliyunSmsModule))]

那么我们在项目中的代码如下所示

 

默认支持两种配置方式,配置文件和SettingManager。下面以配置文件为例,格式为:

{
  "AliyunSmsSettings": {
    "AccessKeyId": "",
    "AccessKeySecret": "",
    "SignName": "",           //SendCodeAsync发送验证码使用
    "TemplateCode": "" ,    //SendCodeAsync发送验证码使用
  } 
}

根据上面的说明,我们在Host项目的AppSettings.json中增加对应的阿里云配置项,如下所示。

 

其中AccessKeyId是标识用户身份的ID,AccessKeySecret 是秘钥,SigName是我们申请的短信商户签名,TemplateCode是我们验证码的配置

 

而短信一般是基于某个模板进行发送的,因此需要确定系统使用的短信模板。

 

阿里云的发送模块是使用ISmsTemplateSender进行发送的,因此在代码中使用如下所示。

那么在使用发送短信验证码的地方,如AccountService应用层中,使用的时候使用它的注入接口即可发送短信验证码了。

 

使用发送短信的操作如下所示。

/// <summary>
        /// 发送短信验证码
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="code">验证码</param>
        /// <returns></returns>
        public async Task<SmsResult> SendCodeAsync(string phone, string code)
        {
            return await _smsTemplateSender.SmsService.SendCodeAsync(phone, code);
        }  
        /// <summary>
        /// 发送模板消息
        /// </summary>
        /// <param name="input">模板对象</param>
        /// <returns></returns>
        public async Task<SmsResult> SendTemplateMessageAsync(SendTemplateMessageInput input)
        {
            return await _smsTemplateSender.SmsService.SendTemplateMessageAsync(input);
        }

 

2、使用自己的阿里云短信发送封装

我之前随笔《使用阿里云的短信服务发送短信》中写过如何处理阿里云短信,虽然那个是常规.net framework的程序中集成的,不过在.net Core的代码都是差不多的。

我们知道ABP框架提供了对应的短信发送接口,一般注入在系统中使用即可。

namespace MyProject.Net
{
    /// <summary>
    /// 短信发送接口
    /// </summary>
    public interface ISmsSender
    {
        Task<CommonResult> SendAsync(string number, string message);
    }
}
那么我们自己定义的短信发送接口,实现它即可,然后注入使用对应的接口即可。
根据

阿里云接口需求,定义一个类似的模型用作加载参数的。

/// <summary>
    /// 阿里云配置参数
    /// </summary>
    internal class AliyunSmsSettting
    {
        public string AccessKeyId { get; set; }
        public string AccessKeySecret { get; set; }
        public string RegionId { get; set; }
        public string EndpointName { get; set; }
        public string Domain { get; set; }
        public string Product { get; set; }
        public string SignName { get; set; }
        public string TemplateCode { get; set; }
        public string TemplateParam { get; set; }
    }

然后让我们的接口实现函数,初始化的时候获取对应的配置信息供使用。

{
    /// <summary>
    /// 使用简单封装,不依赖其他外部模块的阿里云短信发送
    /// </summary>
    public class AliyunSmsSender : IShouldInitialize, ISmsSender, ITransientDependency
    {
        public IConfiguration AppConfiguration { get; set; }
        public IIocManager IocManager { get; set; }
        public ILogger Logger { get; set; }
        private const string Key = "AliyunSmsSettings";
        private const string endpoint = "dysmsapi.aliyuncs.com";
        /// <summary>
        /// 短信配置信息
        /// </summary>
        private AliyunSmsSettting SmsSettings { get; set; }
        public AliyunSmsSender(IConfiguration appConfiguration, IIocManager iocManager)
        {
            this.AppConfiguration = appConfiguration;
            this.IocManager = iocManager;
            this.Logger = NullLogger.Instance;
        }
        public void Initialize()
        {
            this.SmsSettings = GetConfigFromConfigOrSettingsByKey<AliyunSmsSettting>().Result;
        }

然后根据我之前随笔的实现逻辑,给他实现对应的发送操作即可,部分关键代码如下所示

/// <summary>
        /// 发送短信
        /// </summary>
        /// <param name="number">手机号码</param>
        /// <param name="message">消息或验证码</param>
        /// <returns></returns>
        public async Task<CommonResult> SendAsync(string number, string message)
        {
            var result = await PrivateSend(number, message);
            return result;
        }
        /// <summary>
        /// 发送逻辑
        /// </summary>
        /// <returns></returns>
        private async Task<CommonResult> PrivateSend(string number, string code)
        { 
            string nowDate = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'");//GTM时间
            var keyValues = new Dictionary<string, string>();//声明一个字典
            //1.系统参数
            keyValues.Add("SignatureMethod", "HMAC-SHA1");
            keyValues.Add("SignatureNonce", Guid.NewGuid().ToString());
            keyValues.Add("AccessKeyId", this.SmsSettings.AccessKeyId);
            keyValues.Add("SignatureVersion", "1.0");
            keyValues.Add("Timestamp", nowDate);
            keyValues.Add("Format", "Json");//可换成xml
            //2.业务api参数
            keyValues.Add("Action", "SendSms");
            keyValues.Add("Version", "2017-05-25");
            keyValues.Add("RegionId", "cn-hangzhou");
            keyValues.Add("PhoneNumbers", number);
            keyValues.Add("SignName", this.SmsSettings.SignName);
            keyValues.Add("TemplateCode", this.SmsSettings.TemplateCode);
            keyValues.Add("TemplateParam", string.Format("{{\"code\":\"{0}\"}}", code));
            keyValues.Add("OutId", "123");
            //3.去除签名关键字key
            if (keyValues.ContainsKey("Signature"))
            {
                keyValues.Remove("Signature");
            }
            //4.参数key排序
            Dictionary<string, string> ascDic = keyValues.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value.ToString());
            //5.构造待签名的字符串
            var builder = new StringBuilder();
            foreach (var item in ascDic)
            {
                if (item.Key == "SignName")
                {
                }
                else
                {
                    builder.Append("&").Append(specialUrlEncode(item.Key)).Append("=").Append(specialUrlEncode(item.Value));
                }
                if (item.Key == "RegionId")
                {
                    builder.Append("&").Append(specialUrlEncode("SignName")).Append("=").Append(specialUrlEncode(keyValues["SignName"]));
                }
            }
            string sorteQueryString = builder.ToString().Substring(1);
            StringBuilder stringToSign = new StringBuilder();
            stringToSign.Append("GET").Append("&");
            stringToSign.Append(specialUrlEncode("/")).Append("&");
            stringToSign.Append(specialUrlEncode(sorteQueryString));
            string Sign = MySign(this.SmsSettings.AccessKeySecret + "&", stringToSign.ToString());
            //6.签名最后也要做特殊URL编码
            string signture = specialUrlEncode(Sign);
            //最终打印出合法GET请求的URL
            string url = string.Format("http://{0}/?Signature={1}{2}", endpoint, signture, builder);
            var modal = await GetHtmlResult(url);
           return new CommonResult(modal.Success, modal.Message);            
        }

然后在Core模块中初始化的时候,替换对应的短信发送实现即可。

 

这样就可以使用我们自己的短信接口了

 

发送代码如下所示

/// <summary>
        /// 发送短信验证码
        /// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="code">验证码</param>
        /// <returns></returns>
        public async Task<CommonResult> SendSmsCodeAsync(string phone, string code)
        {
            return await _smsSender.SendAsync(phone, code); //使用阿里云接口
        }

 

3、普通短信商的短信发送集成

还有一种我们可能不是基于阿里云,而是其他提供商的接口发送,操作也是自定义短信接口的封装。

我们使用如下参数来确定短信提供商的信息,也可以根据需要自己调整。

 

定义一个配置对应的配置对象,方便获取参数信息。

/// <summary>
    /// 自定义短信配置
    /// </summary>
    internal class MySmsSettings
    {
        /// <summary>
        /// 供应商代码
        /// </summary>
        public string spcode { get; set; }
        /// <summary>
        /// 账户
        /// </summary>
        public string username { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string password { get; set; }
    }

由于我们这个的实现也是基于标准接口ISmsSender的,那么我们实现这个后,也需要特定指定这个实现为ISmsSender的使用。

例如在CoreModule中替换为这个短信实现的话,如下代码。

//使用自定义的 ISmsSender
   Configuration.ReplaceService<ISmsSender, MySmsSender>();

使用接口发送短信的时候,就和我们上面的操作类似的了。

 

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com

相关文章
|
5天前
|
机器学习/深度学习 DataWorks 数据挖掘
基于阿里云Hologres和DataWorks数据集成的方案
基于阿里云Hologres和DataWorks数据集成的方案
23 7
|
10天前
|
存储 SQL 分布式计算
Hologres 与阿里云生态的集成:构建高效的数据处理解决方案
【9月更文第1天】随着大数据时代的到来,数据处理和分析的需求日益增长。阿里云作为国内领先的云计算平台之一,提供了多种数据存储和处理的服务,其中Hologres作为一款实时数仓产品,以其高性能、高可用性以及对标准SQL的支持而受到广泛关注。本文将探讨Hologres如何与阿里云上的其他服务如MaxCompute、DataHub等进行集成,以构建一个完整的数据处理解决方案。
34 2
|
11天前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
25 1
|
11天前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
21 0
|
11天前
|
测试技术 持续交付 开发者
Xamarin 高效移动应用测试最佳实践大揭秘,从框架选择到持续集成,让你的应用质量无敌!
【8月更文挑战第31天】竞争激烈的移动应用市场,Xamarin 作为一款优秀的跨平台开发工具,提供了包括单元测试、集成测试及 UI 测试在内的全面测试方案。借助 Xamarin.UITest 框架,开发者能便捷地用 C# 编写测试案例,如登录功能测试;通过 Xamarin 模拟框架,则可在无需真实设备的情况下模拟各种环境测试应用表现;Xamarin.TestCloud 则支持在真实设备上执行自动化测试,确保应用兼容性。结合持续集成与部署策略,进一步提升测试效率与应用质量。掌握 Xamarin 的测试最佳实践,对确保应用稳定性和优化用户体验至关重要。
24 0
|
11天前
|
数据库 Java 数据库连接
Struts 2 与 Hibernate 的完美邂逅:如何无缝集成两大框架,轻松玩转高效 CRUD 操作?
【8月更文挑战第31天】本文通过具体示例介绍了如何在 Struts 2 中整合 Hibernate,实现基本的 CRUD 操作。首先创建 Maven 项目并添加相关依赖,接着配置 Hibernate 并定义实体类及其映射文件。然后创建 DAO 接口及实现类处理数据库操作,再通过 Struts 2 的 Action 类处理用户请求。最后配置 `struts.xml` 文件并创建 JSP 页面展示用户列表及编辑表单。此示例展示了如何配置和使用这两个框架,使代码更加模块化和可维护。
21 0
|
11天前
|
Java 数据库连接 数据库
强强联手!JSF 与 Hibernate 打造高效数据访问层,让你的应用如虎添翼,性能飙升!
【8月更文挑战第31天】本文通过具体示例详细介绍了如何在 JavaServer Faces (JSF) 应用程序中集成 Hibernate,实现数据访问层的最佳实践。首先,创建一个 JSF 项目并在 Eclipse 中配置支持 JSF 的服务器版本。接着,添加 JSF 和 Hibernate 依赖,并配置数据库连接池和 Hibernate 配置文件。然后,定义实体类 `User` 和 DAO 类 `UserDAO` 处理数据库操作。
31 0
|
12天前
|
机器学习/深度学习 PyTorch TensorFlow
NumPy 与机器学习框架的集成
【8月更文第30天】NumPy 是 Python 中用于科学计算的核心库之一,它提供了高效的多维数组对象,以及用于操作数组的大量函数。NumPy 的高效性和灵活性使其成为许多机器学习框架的基础。本文将探讨 NumPy 如何与 TensorFlow 和 PyTorch 等流行机器学习框架协同工作,并通过具体的代码示例来展示它们之间的交互。
12 0
|
2月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
159 6
|
2月前
|
Java 关系型数据库 MySQL
如何实现Springboot+camunda+mysql的集成
【7月更文挑战第2天】集成Spring Boot、Camunda和MySQL的简要步骤: 1. 初始化Spring Boot项目,添加Camunda和MySQL驱动依赖。 2. 配置`application.properties`,包括数据库URL、用户名和密码。 3. 设置Camunda引擎属性,指定数据源。 4. 引入流程定义文件(如`.bpmn`)。 5. 创建服务处理流程操作,创建控制器接收请求。 6. Camunda自动在数据库创建表结构。 7. 启动应用,测试流程启动,如通过服务和控制器开始流程实例。 示例代码包括服务类启动流程实例及控制器接口。实际集成需按业务需求调整。
180 4