欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一个包含动态变量的短信模板设计分析

程序员文章站 2022-07-11 08:51:05
在给客户开发一个信息发送功能的时候,需要涉及到短信的发送,短信发送一般不同的厂商提供的接口不同,处理方式也不太一样,之前用的一个厂商的,提供了一个封装类就很容易发送短息,因此都是基于HTTP协议做的一个数据发送而已,接触阿里云的短信服务器后,发现阿里云还增加了非常多的参数,其中包括一些秘钥和签名的内... ......

在给客户开发一个信息发送功能的时候,需要涉及到短信的发送,短信发送一般不同的厂商提供的接口不同,处理方式也不太一样,之前用的一个厂商的,提供了一个封装类就很容易发送短息,因此都是基于http协议做的一个数据发送而已,接触阿里云的短信服务器后,发现阿里云还增加了非常多的参数,其中包括一些秘钥和签名的内容。短信发送由于比较敏感原因,大多数应用场景是验证码或者一些固定的信息提醒,因此厂商都要求客户按预定的模板来发送,这样限制了短信的应用场景,只能根据业务进行消息定制了。本篇随笔主要介绍阿里云的短信服务的发送处理。

1、短信发送的处理介绍

在短信发送中,阿里云提供自己的sdk封装,以降低使用的难度,不过需要引入它提供的sdk类库;本篇随笔主要介绍基于http方式进行自行的封装处理,这部分代码我从网上摘录并进行一定的调整,测试成功。

使用阿里云的短信服务,需要注册登录自己的阿里云控制台,然后进入accesskeys的处理界面

一个包含动态变量的短信模板设计分析

然后系统会提示需要创建一个新的key(如果没有的话就创建,否则使用已有的即可)

一个包含动态变量的短信模板设计分析

这里我们获取到accesskey id 和access key secret两个关键信息,需要用在数据签名的里面的。

另外我们需要创建一个signname,也就是签名,一般为我们短信提示的公司名称,如【广州爱奇迪】这样的字样。

一个包含动态变量的短信模板设计分析

短信是基于模板的,阿里云不能发送随意的内容,因此只能基于模板发送,如验证码或者业务消息,有点类似微信的模板消息了,因此里面可以添加变量发送的。

记得我以前写过一个关于动态变量的信息发送的文章《》,就是介绍如何处理变量模板消息的。

阿里云默认提供了一些基础模板,如下所示。

一个包含动态变量的短信模板设计分析

一般我们业务可能还需要定制一些业务消息,那么需要审核通过才可以使用新增的模板消息。

短信的发送可以利用api接口进行发送,如下所示是它的api说明

一个包含动态变量的短信模板设计分析

如果需要采集用户的回复信息,如一些随访记录,那么需要做一个接口的处理,如下所示。

一个包含动态变量的短信模板设计分析 

发送短信的api接口详细说明如下所示。

一个包含动态变量的短信模板设计分析

其实请求信息比上面列出的信息多很多,包括秘钥和数据加密信息等的处理,下面详细给出代码说明。 

 

2、模板消息发送

 有了上面的信息介绍,我们大概了解了短信消息发送的处理规则了。

实际上,发送信息的时候,我们可能需要添加很多参数信息,如下代码所示。

            dictionary<string, string> keyvalues = new dictionary<string, string>();//声明一个字典
            //1.系统参数
            keyvalues.add("signaturemethod", "hmac-sha1");
            keyvalues.add("signaturenonce", guid.newguid().tostring());
            keyvalues.add("accesskeyid", 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", mobile);
            keyvalues.add("signname", signname);
            keyvalues.add("templateparam", "{\"code\":\"" + code + "\"}");
            keyvalues.add("templatecode", templatecode);
            keyvalues.add("outid", "123");

具体我们来贴出不用sdk的短信发送辅助类,如下代码所示。

    /// <summary>
    /// 阿里短信发送
    /// </summary>
    public class smshelper
    {
        private const string endpoint = "dysmsapi.aliyuncs.com";
        private const string accesskeyid = "你的秘钥键";
        private const string accesskeysecret = "你的秘钥值";
        private const string signname = "广州爱奇迪";

        /// <summary>
        /// 短信验证码
        /// </summary>
        /// <param name="mobile"></param>
        /// <param name="code"></param>
        /// <returns></returns>
        public static string sendsms(string mobile, int code, string templatecode = "sms_126645400")
        {
            string nowdate = datetime.now.touniversaltime().tostring("yyyy-mm-dd't'hh:mm:ss'z'");//gtm时间
            dictionary<string, string> keyvalues = new dictionary<string, string>();//声明一个字典
            //1.系统参数
            keyvalues.add("signaturemethod", "hmac-sha1");
            keyvalues.add("signaturenonce", guid.newguid().tostring());
            keyvalues.add("accesskeyid", 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", mobile);
            keyvalues.add("signname", signname);
            keyvalues.add("templateparam", "{\"code\":\"" + code + "\"}");
            keyvalues.add("templatecode", templatecode);
            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.构造待签名的字符串
            stringbuilder 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(accesskeysecret + "&", stringtosign.tostring());
            //6.签名最后也要做特殊url编码
            string signture = specialurlencode(sign);

            //最终打印出合法get请求的url
            string url = string.format("http://{0}/?signature={1}{2}", endpoint, signture, builder);
            string result = gethtmlformurl(url);
            return result;
        }


        /// <summary>
        /// 短信接口c#调用方法
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private static string gethtmlformurl(string url)
        {
            string strret = null;
            if (url == null || url.trim().tostring() == "")
            {
                return strret;
            }
            string targeturl = url.trim().tostring();
            try
            {
                httpwebrequest hr = (httpwebrequest)webrequest.create(targeturl);
                hr.useragent = "mozilla/4.0 (compatible; msie 6.0; windows nt 5.1)";
                hr.method = "get";
                hr.timeout = 30 * 60 * 1000;
                webresponse hs = hr.getresponse();
                stream sr = hs.getresponsestream();
                streamreader ser = new streamreader(sr, encoding.utf8);

                strret = messagehandle(ser.readtoend());
            }
            catch (exception ex)
            {
                strret = "短信发送失败!" + ex.message;
            }
            return strret;
        }

        /// <summary>
        /// 验证手机号码是否合法
        /// </summary>
        /// <param name="mobile">电话号码</param>
        /// <returns></returns>
        public static bool ismobile(string mobile)
        {
            return system.text.regularexpressions.regex.ismatch(mobile, @"^1[3|4|5|7|8][0-9]\d{8}$");
        }

        /// <summary>
        /// url编码
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static string specialurlencode(string temp)
        {
            stringbuilder stringbuilder = new stringbuilder();
            for (int i = 0; i < temp.length; i++)
            {
                string t = temp[i].tostring();
                string k = httputility.urlencode(t, encoding.utf8);
                if (t == k)
                {
                    stringbuilder.append(t);
                }
                else
                {
                    stringbuilder.append(k.toupper());
                }
            }
            return stringbuilder.tostring().replace("+", "%20").replace("*", "%2a").replace("%7e", "~");
        }

        /// <summary>
        /// hmacsha1签名
        /// </summary>
        /// <param name="accesssecret"></param>
        /// <param name="stringtosign"></param>
        /// <returns></returns>
        private static string mysign(string accesssecret, string stringtosign)
        {
            try
            {
                var hmacsha1 = new hmacsha1(encoding.utf8.getbytes(accesssecret));
                var databuffer = encoding.utf8.getbytes(stringtosign);
                var hashbytes = hmacsha1.computehash(databuffer);
                string stringbyte = bitconverter.tostring(hashbytes, 0).replace("-", string.empty).tolower();
                byte[] bytes = strtotohexbyte(stringbyte);
                return convert.tobase64string(bytes);
            }
            catch (exception ex)
            {

                throw ex;
            }
        }
        /// <summary>
        /// 字符串转16进制字节数组
        /// </summary>
        /// <param name="hexstring"></param>
        /// <returns></returns>
        private static byte[] strtotohexbyte(string hexstring)
        {
            hexstring = hexstring.replace(" ", "");
            if ((hexstring.length % 2) != 0)
                hexstring += " ";
            byte[] returnbytes = new byte[hexstring.length / 2];
            for (int i = 0; i < returnbytes.length; i++)
                returnbytes[i] = convert.tobyte(hexstring.substring(i * 2, 2), 16);
            return returnbytes;
        }

        /// <summary>
        /// 消息处理机制
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        private static string messagehandle(string str)
        {
            messagemodel message = jsonconvert.deserializeobject<messagemodel>(str);
            string result = "";
            switch (message.code)
            {
                case "ok":
                    result = "短信发送成功!";
                    break;
                case "isp.ram_permission_deny":
                    result = "ram权限deny";
                    break;
                case "isv.out_of_service":
                    result = "业务停机";
                    break;
                case "isv.product_un_subscript":
                    result = "未开通云通信产品的阿里云客户";
                    break;
                case "isv.product_unsubscribe":
                    result = "产品未开通";
                    break;
                case "isv.account_not_exists":
                    result = "账户不存在";
                    break;
                case "isv.account_abnormal":
                    result = "账户异常    ";
                    break;
                case "isv.sms_template_illegal":
                    result = "短信模板不合法";
                    break;
                case "isv.sms_signature_illegal":
                    result = "短信签名不合法";
                    break;
                case "isv.invalid_parameters":
                    result = "参数异常";
                    break;
                case "isv.mobile_number_illegal":
                    result = "非法手机号";
                    break;
                case "isv.mobile_count_over_limit":
                    result = "手机号码数量超过限制";
                    break;
                case "isv.template_missing_parameters":
                    result = "模板缺少变量";
                    break;
                case "isv.business_limit_control":
                    result = "业务限流";
                    break;
                case "isv.invalid_json_param":
                    result = "json参数不合法,只接受字符串值";
                    break;
                case "isv.param_length_limit":
                    result = "参数超出长度限制";
                    break;
                case "isv.param_not_support_url":
                    result = "不支持url";
                    break;
                case "isv.amount_not_enough":
                    result = "账户余额不足";
                    break;
                case "isv.template_params_illegal":
                    result = "模板变量里包含非法关键字";
                    break;
            }
            return result;
        }

    }

    internal class messagemodel
    {
        public string requestid { get; set; }
        public string code { get; set; }
        public string message { get; set; }
    }

上面代码不是我原创,声明一下,我做了一些修改调整而已,方便辅助类的使用,我们输入我们的企业的秘钥键值,然后发送测试短信即可。

            string tel = "18620292076";
            string result = smshelper.sendsms(tel, 123456);
            console.writeline(result);

发送测试,3~5秒就可以收到验证码信息的提示了,如下所示。

一个包含动态变量的短信模板设计分析

 以上就是短信消息的发送,希望对使用阿里云短信服务的开发人员有所帮助,辅助类直接就可以使用了。