一个包含动态变量的短信模板设计分析
在给客户开发一个信息发送功能的时候,需要涉及到短信的发送,短信发送一般不同的厂商提供的接口不同,处理方式也不太一样,之前用的一个厂商的,提供了一个封装类就很容易发送短息,因此都是基于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秒就可以收到验证码信息的提示了,如下所示。
以上就是短信消息的发送,希望对使用阿里云短信服务的开发人员有所帮助,辅助类直接就可以使用了。
上一篇: 深入php多态的实现详解
下一篇: PHP输出XML到页面的3种方法详解