阿里云官方的skd(aliyun-net-sdk-core,aliyun-net-sdk-dysmsapi)在dnc中发送短信会出错,nuget上的包貌似也一样不管用。直接改下sdk当然也可以,但就发个短信,官方的sdk实在是有点繁杂,其实可以简单化,一个类就搞定。
1 using Newtonsoft.Json; 2 using System; 3 using System.Collections.Generic; 4 using System.Globalization; 5 using System.Net; 6 using System.Net.Http; 7 using System.Security.Cryptography; 8 using System.Text; 9 using System.Threading.Tasks; 10 using System.Web; 11 12 namespace Vino.Core.Communication.SMS 13 { 14 public class AliyunSmsSender : ISmsSender 15 { 16 private string RegionId = "cn-hangzhou"; 17 private string Version = "2017-05-25"; 18 private string Action = "SendSms"; 19 private string Format = "JSON"; 20 private string Domain = "dysmsapi.aliyuncs.com"; 21 22 private int MaxRetryNumber = 3; 23 private bool AutoRetry = true; 24 private const string SEPARATOR = "&"; 25 private int TimeoutInMilliSeconds = 100000; 26 27 private string AccessKeyId; 28 private string AccessKeySecret; 29 30 public AliyunSmsSender(string accessKeyId, string accessKeySecret) 31 { 32 this.AccessKeyId = accessKeyId; 33 this.AccessKeySecret = accessKeySecret; 34 } 35 36 ///37 /// 发送短信 38 /// 39 public async Task<(bool success, string response)> Send(SmsObject sms) 40 { 41 var paramers = new Dictionary(); 42 paramers.Add("PhoneNumbers", sms.Mobile); 43 paramers.Add("SignName", sms.Signature); 44 paramers.Add("TemplateCode", sms.TempletKey); 45 paramers.Add("TemplateParam", JsonConvert.SerializeObject(sms.Data)); 46 paramers.Add("OutId", sms.OutId); 47 paramers.Add("AccessKeyId", AccessKeyId); 48 49 try 50 { 51 string url = GetSignUrl(paramers, AccessKeySecret); 52 53 int retryTimes = 1; 54 var reply = await HttpGetAsync(url); 55 while (500 <= reply.StatusCode && AutoRetry && retryTimes < MaxRetryNumber) 56 { 57 url = GetSignUrl(paramers, AccessKeySecret); 58 reply = await HttpGetAsync(url); 59 retryTimes++; 60 } 61 62 if (!string.IsNullOrEmpty(reply.response)) 63 { 64 var res = JsonConvert.DeserializeObject >(reply.response); 65 if (res != null && res.ContainsKey("Code") && "OK".Equals(res["Code"])) 66 { 67 return (true, response: reply.response); 68 } 69 } 70 71 return (false, response: reply.response); 72 } 73 catch (Exception ex) 74 { 75 return (false, response: ex.Message); 76 } 77 } 78 79 private string GetSignUrl(Dictionary parameters, string accessSecret) 80 { 81 var imutableMap = new Dictionary (parameters); 82 imutableMap.Add("Timestamp", FormatIso8601Date(DateTime.Now)); 83 imutableMap.Add("SignatureMethod", "HMAC-SHA1"); 84 imutableMap.Add("SignatureVersion", "1.0"); 85 imutableMap.Add("SignatureNonce", Guid.NewGuid().ToString()); 86 imutableMap.Add("Action", Action); 87 imutableMap.Add("Version", Version); 88 imutableMap.Add("Format", Format); 89 imutableMap.Add("RegionId", RegionId); 90 91 IDictionary sortedDictionary = new SortedDictionary (imutableMap, StringComparer.Ordinal); 92 StringBuilder canonicalizedQueryString = new StringBuilder(); 93 foreach (var p in sortedDictionary) 94 { 95 canonicalizedQueryString.Append("&") 96 .Append(PercentEncode(p.Key)).Append("=") 97 .Append(PercentEncode(p.Value)); 98 } 99 100 StringBuilder stringToSign = new StringBuilder();101 stringToSign.Append("GET");102 stringToSign.Append(SEPARATOR);103 stringToSign.Append(PercentEncode("/"));104 stringToSign.Append(SEPARATOR);105 stringToSign.Append(PercentEncode(canonicalizedQueryString.ToString().Substring(1)));106 107 string signature = SignString(stringToSign.ToString(), accessSecret + "&");108 109 imutableMap.Add("Signature", signature);110 111 return ComposeUrl(Domain, imutableMap);112 }113 114 private static string FormatIso8601Date(DateTime date)115 {116 return date.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'", CultureInfo.CreateSpecificCulture("en-US"));117 }118 119 /// 120 /// 签名121 /// 122 public static string SignString(string source, string accessSecret)123 {124 using (var algorithm = new HMACSHA1(Encoding.UTF8.GetBytes(accessSecret.ToCharArray())))125 {126 return Convert.ToBase64String(algorithm.ComputeHash(Encoding.UTF8.GetBytes(source.ToCharArray())));127 }128 }129 130 private static string ComposeUrl(string endpoint, Dictionaryparameters)131 {132 StringBuilder urlBuilder = new StringBuilder("");133 urlBuilder.Append("http://").Append(endpoint);134 if (-1 == urlBuilder.ToString().IndexOf("?"))135 {136 urlBuilder.Append("/?");137 }138 string query = ConcatQueryString(parameters);139 return urlBuilder.Append(query).ToString();140 }141 142 private static string ConcatQueryString(Dictionary parameters)143 {144 if (null == parameters)145 {146 return null;147 }148 StringBuilder sb = new StringBuilder();149 150 foreach (var entry in parameters)151 {152 String key = entry.Key;153 String val = entry.Value;154 155 sb.Append(HttpUtility.UrlEncode(key, Encoding.UTF8));156 if (val != null)157 {158 sb.Append("=").Append(HttpUtility.UrlEncode(val, Encoding.UTF8));159 }160 sb.Append("&");161 }162 163 int strIndex = sb.Length;164 if (parameters.Count > 0)165 sb.Remove(strIndex - 1, 1);166 167 return sb.ToString();168 }169 170 public static string PercentEncode(string value)171 {172 StringBuilder stringBuilder = new StringBuilder();173 string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";174 byte[] bytes = Encoding.GetEncoding("UTF-8").GetBytes(value);175 foreach (char c in bytes)176 {177 if (text.IndexOf(c) >= 0)178 {179 stringBuilder.Append(c);180 }181 else182 {183 stringBuilder.Append("%").Append(184 string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c));185 }186 }187 return stringBuilder.ToString();188 }189 190 private async Task<(int StatusCode, string response)> HttpGetAsync(string url)191 {192 HttpClientHandler handler = new HttpClientHandler();193 handler.Proxy = null;194 handler.AutomaticDecompression = DecompressionMethods.GZip;195 196 using (var http = new HttpClient(handler))197 {198 http.Timeout = new TimeSpan(TimeSpan.TicksPerMillisecond * TimeoutInMilliSeconds);199 HttpResponseMessage response = await http.GetAsync(url);200 return ((int)response.StatusCode, await response.Content.ReadAsStringAsync());201 }202 }203 }204 }
SmsObject.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace Vino.Core.Communication.SMS 6 { 7 public class SmsObject 8 { 9 ///10 /// 手机号11 /// 12 public string Mobile { set; get; }13 14 ///15 /// 签名16 /// 17 public string Signature { get; set; }18 19 ///20 /// 模板Key21 /// 22 public string TempletKey { set; get; }23 24 ///25 /// 短信数据26 /// 27 public IDictionaryData { set; get; }28 29 /// 30 /// 业务ID31 /// 32 public string OutId { set; get; }33 }34 }
调用方法
1 IDictionarydata = new Dictionary (); 2 data.Add("code", "111111"); 3 var sms = new SmsObject 4 { 5 Mobile = "18812345678", 6 Signature = "我的签名", 7 TempletKey = "模板ID", 8 Data = data, 9 OutId = "OutId"10 };11 12 var res = await new AliyunSmsSender(accessKeyId, accessKeySecret).Send(sms);13 Debug.WriteLine($"发送结果:{res.success}");14 Debug.WriteLine($"Response:{res.response}");