DefaultAliyunClient.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. using Aliyun.Api.Parser;
  2. using Aliyun.Api.Util;
  3. using FastJSON;
  4. using System;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Globalization;
  8. using System.Security.Cryptography;
  9. using System.Text;
  10. using System.Xml;
  11. using Top.Api;
  12. using Top.Api.Util;
  13. namespace Aliyun.Api
  14. {
  15. /// <summary>
  16. /// 基于REST的阿里云客户端。
  17. /// </summary>
  18. public class DefaultAliyunClient : IAliyunClient
  19. {
  20. public const string FORMAT_XML = "xml";
  21. public const string FORMAT_JSON = "json";
  22. public const string HTTP_METHOD_POST = "POST";
  23. public const string SDK_VERSION = "top-sdk-net-dynamicVersionNo"; // SDK自动生成会替换成真实的版本
  24. private AliyunWebUtils webUtils;
  25. private ITopLogger topLogger;
  26. private bool disableParser = false; // 禁用响应结果解释
  27. private bool disableTrace = false; // 禁用日志调试功能
  28. private IDictionary<string, string> systemParameters; // 设置所有请求共享的系统级参数
  29. private const string ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
  30. private const string ENCODING_UTF8 = "UTF-8";
  31. private string httpMethod = HTTP_METHOD_POST;
  32. private string format = FORMAT_XML;
  33. private string serverUrl;
  34. private string accessKeyId;
  35. private string accessKeySecret;
  36. #region DefaultTopClient Constructors
  37. public DefaultAliyunClient(string serverUrl, string accessKeyId, string accessKeySecret)
  38. {
  39. this.accessKeyId = accessKeyId;
  40. this.accessKeySecret = accessKeySecret;
  41. this.serverUrl = serverUrl;
  42. this.webUtils = new AliyunWebUtils();
  43. this.topLogger = Top.Api.Log.Instance;
  44. }
  45. public DefaultAliyunClient(string serverUrl, string accessKeyId, string accessKeySecret, string format)
  46. : this(serverUrl, accessKeyId, accessKeySecret)
  47. {
  48. this.format = format;
  49. }
  50. #endregion
  51. public void SetTopLogger(ITopLogger topLogger)
  52. {
  53. this.topLogger = topLogger;
  54. }
  55. public void SetTimeout(int timeout)
  56. {
  57. this.webUtils.Timeout = timeout;
  58. }
  59. public void SetDisableParser(bool disableParser)
  60. {
  61. this.disableParser = disableParser;
  62. }
  63. public void SetDisableTrace(bool disableTrace)
  64. {
  65. this.disableTrace = disableTrace;
  66. }
  67. public void SetSystemParameters(IDictionary<string, string> systemParameters)
  68. {
  69. this.systemParameters = systemParameters;
  70. }
  71. #region ITopClient Members
  72. public T Execute<T>(IAliyunRequest<T> request) where T : AliyunResponse
  73. {
  74. return Execute<T>(request, null);
  75. }
  76. public T Execute<T>(IAliyunRequest<T> request, string session) where T : AliyunResponse
  77. {
  78. return Execute<T>(request, session, DateTime.Now);
  79. }
  80. public T Execute<T>(IAliyunRequest<T> request, string session, DateTime timestamp) where T : AliyunResponse
  81. {
  82. return DoExecute<T>(request, session, timestamp);
  83. }
  84. #endregion
  85. private T DoExecute<T>(IAliyunRequest<T> request, string session, DateTime timestamp) where T : AliyunResponse
  86. {
  87. // 提前检查业务参数
  88. try
  89. {
  90. request.Validate();
  91. }
  92. catch (TopException e)
  93. {
  94. return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
  95. }
  96. // 添加协议级请求参数
  97. TopDictionary txtParams = new TopDictionary(request.GetParameters());
  98. txtParams.AddAll(this.systemParameters);
  99. AddCommonParams(request, txtParams);
  100. string reqUrl = webUtils.BuildGetUrl(this.serverUrl, txtParams);
  101. try
  102. {
  103. string body;
  104. if (request is IAliyunUploadRequest<T>) // 是否需要上传文件
  105. {
  106. IAliyunUploadRequest<T> uRequest = (IAliyunUploadRequest<T>)request;
  107. IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
  108. body = webUtils.DoPost(this.serverUrl.TrimEnd('/'), txtParams, fileParams);
  109. }
  110. else
  111. {
  112. body = webUtils.DoPost(this.serverUrl.TrimEnd('/'), txtParams);
  113. }
  114. // 解释响应结果
  115. T rsp;
  116. if (disableParser)
  117. {
  118. rsp = Activator.CreateInstance<T>();
  119. rsp.Body = body;
  120. }
  121. else
  122. {
  123. if (FORMAT_XML.Equals(format))
  124. {
  125. IAliyunParser tp = new AliyunXmlParser();
  126. rsp = tp.Parse<T>(body);
  127. }
  128. else
  129. {
  130. IAliyunParser tp = new AliyunJsonParser();
  131. rsp = tp.Parse<T>(body);
  132. }
  133. }
  134. // 追踪错误的请求
  135. if (!disableTrace && rsp.IsError)
  136. {
  137. StringBuilder sb = new StringBuilder(reqUrl).Append(" response error!\r\n").Append(rsp.Body);
  138. topLogger.Warn(sb.ToString());
  139. }
  140. return rsp;
  141. }
  142. catch (Exception e)
  143. {
  144. if (!disableTrace)
  145. {
  146. StringBuilder sb = new StringBuilder(reqUrl).Append(" request error!\r\n").Append(e.StackTrace);
  147. topLogger.Error(sb.ToString());
  148. }
  149. throw e;
  150. }
  151. }
  152. private T CreateErrorResponse<T>(string errCode, string errMsg) where T : AliyunResponse
  153. {
  154. T rsp = Activator.CreateInstance<T>();
  155. rsp.Code = errCode;
  156. rsp.Message = errMsg;
  157. if (FORMAT_XML.Equals(format))
  158. {
  159. XmlDocument root = new XmlDocument();
  160. XmlElement bodyE = root.CreateElement("Error");
  161. XmlElement codeE = root.CreateElement("Code");
  162. codeE.InnerText = errCode;
  163. bodyE.AppendChild(codeE);
  164. XmlElement msgE = root.CreateElement("Message");
  165. msgE.InnerText = errMsg;
  166. bodyE.AppendChild(msgE);
  167. root.AppendChild(bodyE);
  168. rsp.Body = root.OuterXml;
  169. }
  170. else
  171. {
  172. IDictionary<string, object> errObj = new Dictionary<string, object>();
  173. errObj.Add("Code", errCode);
  174. errObj.Add("Message", errMsg);
  175. string body = JSON.ToJSON(errObj);
  176. rsp.Body = body;
  177. }
  178. return rsp;
  179. }
  180. private void AddCommonParams<T>(IAliyunRequest<T> request, TopDictionary parameters) where T : AliyunResponse
  181. {
  182. String[] strArray = request.GetApiName().Split('.');
  183. if (strArray.Length < 5)
  184. {
  185. throw new TopException("Wrong api name.");
  186. }
  187. String action = strArray[3];
  188. parameters.Add("Action", action);
  189. String version = strArray[4];
  190. parameters.Add("Version", version);
  191. parameters.Add("AccessKeyId", accessKeyId);
  192. parameters.Add("Timestamp", FormatIso8601Date(DateTime.Now));
  193. parameters.Add("SignatureMethod", "HMAC-SHA1");
  194. parameters.Add("SignatureVersion", "1.0");
  195. parameters.Add("SignatureNonce", Guid.NewGuid().ToString()); // 可以使用GUID作为SignatureNonce
  196. parameters.Add("Format", format);
  197. // 计算签名,并将签名结果加入请求参数中
  198. parameters.Add("Signature", ComputeSignature(parameters));
  199. }
  200. private String ComputeSignature(TopDictionary parameters)
  201. {
  202. const String SEPARATOR = "&";
  203. // 生成规范化请求字符串
  204. StringBuilder canonicalizedQueryString = new StringBuilder();
  205. var orderedParameters = SortDictionary(parameters);
  206. foreach (var p in orderedParameters)
  207. {
  208. canonicalizedQueryString.Append("&")
  209. .Append(PercentEncode(p.Key)).Append("=")
  210. .Append(PercentEncode(p.Value));
  211. }
  212. // 生成用于计算签名的字符串 stringToSign
  213. StringBuilder stringToSign = new StringBuilder();
  214. stringToSign.Append(httpMethod).Append(SEPARATOR);
  215. stringToSign.Append(PercentEncode("/")).Append(SEPARATOR);
  216. stringToSign.Append(PercentEncode(
  217. canonicalizedQueryString.ToString().Substring(1)));
  218. // 注意accessKeySecret后面要加入一个字符"&"
  219. String signature = CalculateSignature(accessKeySecret + "&",
  220. stringToSign.ToString());
  221. return signature;
  222. }
  223. private static String FormatIso8601Date(DateTime date)
  224. {
  225. // 注意使用UTC时间
  226. return date.ToUniversalTime().ToString(ISO8601_DATE_FORMAT, CultureInfo.CreateSpecificCulture("en-US"));
  227. }
  228. private static String CalculateSignature(String key, String stringToSign)
  229. {
  230. // 使用HmacSHA1算法计算HMAC值
  231. using (var algorithm = KeyedHashAlgorithm.Create("HMACSHA1"))
  232. {
  233. algorithm.Key = Encoding.GetEncoding(ENCODING_UTF8).GetBytes(key.ToCharArray());
  234. return Convert.ToBase64String(
  235. algorithm.ComputeHash(
  236. Encoding.GetEncoding(ENCODING_UTF8).GetBytes(stringToSign.ToCharArray())));
  237. }
  238. }
  239. private static string PercentEncode(String value)
  240. {
  241. StringBuilder stringBuilder = new StringBuilder();
  242. string text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
  243. byte[] bytes = Encoding.GetEncoding(ENCODING_UTF8).GetBytes(value);
  244. foreach (char c in bytes)
  245. {
  246. if (text.IndexOf(c) >= 0)
  247. {
  248. stringBuilder.Append(c);
  249. }
  250. else
  251. {
  252. stringBuilder.Append("%").Append(
  253. string.Format(CultureInfo.InvariantCulture, "{0:X2}", (int)c));
  254. }
  255. }
  256. return stringBuilder.ToString();
  257. }
  258. private static Dictionary<string, string> SortDictionary(Dictionary<string, string> dic)
  259. {
  260. ArrayList arrayList = new ArrayList(dic.Keys);
  261. arrayList.Sort(StringComparer.Ordinal);
  262. Dictionary<string, string> sortedDictionary = new Dictionary<string, string>();
  263. foreach (string key in arrayList)
  264. {
  265. sortedDictionary.Add(key, dic[key]);
  266. }
  267. return sortedDictionary;
  268. }
  269. }
  270. }