DefaultTopClient.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using FastJSON;
  2. using System;
  3. using System.Collections;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Xml;
  7. using Top.Api.Parser;
  8. using Top.Api.Util;
  9. namespace Top.Api
  10. {
  11. /// <summary>
  12. /// 基于REST的TOP客户端。
  13. /// </summary>
  14. public class DefaultTopClient : ITopClient
  15. {
  16. internal string serverUrl;
  17. internal string appKey;
  18. internal string appSecret;
  19. internal string format = Constants.FORMAT_XML;
  20. internal WebUtils webUtils;
  21. internal ITopLogger topLogger;
  22. internal bool disableParser = false; // 禁用响应结果解释
  23. internal bool disableTrace = false; // 禁用日志调试功能
  24. internal bool useSimplifyJson = false; // 是否采用精简化的JSON返回
  25. internal bool useGzipEncoding = true; // 是否启用响应GZIP压缩
  26. internal IDictionary<string, string> systemParameters; // 设置所有请求共享的系统级参数
  27. #region DefaultTopClient Constructors
  28. public DefaultTopClient(string serverUrl, string appKey, string appSecret)
  29. {
  30. this.appKey = appKey;
  31. this.appSecret = appSecret;
  32. this.serverUrl = serverUrl;
  33. this.webUtils = new WebUtils();
  34. this.topLogger = Top.Api.Log.Instance;
  35. }
  36. public DefaultTopClient(string serverUrl, string appKey, string appSecret, string format)
  37. : this(serverUrl, appKey, appSecret)
  38. {
  39. this.format = format;
  40. }
  41. #endregion
  42. public void SetProxy(WebProxy proxy)
  43. {
  44. this.webUtils.Proxy = proxy;
  45. }
  46. public void SetTimeout(int timeout)
  47. {
  48. this.webUtils.Timeout = timeout;
  49. }
  50. public void SetReadWriteTimeout(int readWriteTimeout)
  51. {
  52. this.webUtils.ReadWriteTimeout = readWriteTimeout;
  53. }
  54. public void SetDisableParser(bool disableParser)
  55. {
  56. this.disableParser = disableParser;
  57. }
  58. public void SetDisableTrace(bool disableTrace)
  59. {
  60. this.disableTrace = disableTrace;
  61. }
  62. public void SetUseSimplifyJson(bool useSimplifyJson)
  63. {
  64. this.useSimplifyJson = useSimplifyJson;
  65. }
  66. public void SetUseGzipEncoding(bool useGzipEncoding)
  67. {
  68. this.useGzipEncoding = useGzipEncoding;
  69. }
  70. public void SetIgnoreSSLCheck(bool ignore)
  71. {
  72. this.webUtils.IgnoreSSLCheck = ignore;
  73. }
  74. /// <summary>
  75. /// 禁用本地代理自动检测功能,加快连接建立速度
  76. /// </summary>
  77. public void SetDisableWebProxy(bool disable)
  78. {
  79. this.webUtils.DisableWebProxy = disable;
  80. }
  81. /// <summary>
  82. /// 设置单个网站最大并发连接数,桌面端默认是2,服务器端默认是10,对于桌面端并发请求多的,可以适当调高。
  83. /// </summary>
  84. /// <param name="limit"></param>
  85. public void SetMaxConnectionLimit(int limit)
  86. {
  87. System.Net.ServicePointManager.DefaultConnectionLimit = limit;
  88. }
  89. public void SetSystemParameters(IDictionary<string, string> systemParameters)
  90. {
  91. this.systemParameters = systemParameters;
  92. }
  93. #region ITopClient Members
  94. public virtual T Execute<T>(ITopRequest<T> request) where T : TopResponse
  95. {
  96. return DoExecute<T>(request, null, DateTime.Now);
  97. }
  98. public virtual T Execute<T>(ITopRequest<T> request, string session) where T : TopResponse
  99. {
  100. return DoExecute<T>(request, session, DateTime.Now);
  101. }
  102. public virtual T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
  103. {
  104. return DoExecute<T>(request, session, timestamp);
  105. }
  106. #endregion
  107. private T DoExecute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
  108. {
  109. long start = DateTime.Now.Ticks;
  110. // 提前检查业务参数
  111. try
  112. {
  113. request.Validate();
  114. }
  115. catch (TopException e)
  116. {
  117. return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
  118. }
  119. // 添加协议级请求参数
  120. TopDictionary txtParams = new TopDictionary(request.GetParameters());
  121. txtParams.Add(Constants.METHOD, request.GetApiName());
  122. txtParams.Add(Constants.VERSION, "2.0");
  123. txtParams.Add(Constants.SIGN_METHOD, Constants.SIGN_METHOD_HMAC);
  124. txtParams.Add(Constants.APP_KEY, appKey);
  125. txtParams.Add(Constants.FORMAT, format);
  126. txtParams.Add(Constants.PARTNER_ID, GetSdkVersion());
  127. txtParams.Add(Constants.TIMESTAMP, timestamp);
  128. txtParams.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey());
  129. txtParams.Add(Constants.SESSION, session);
  130. txtParams.AddAll(this.systemParameters);
  131. if (this.useSimplifyJson)
  132. {
  133. txtParams.Add(Constants.SIMPLIFY, "true");
  134. }
  135. // 添加签名参数
  136. txtParams.Add(Constants.SIGN, TopUtils.SignTopRequest(txtParams, appSecret, Constants.SIGN_METHOD_HMAC));
  137. // 添加头部参数
  138. if (this.useGzipEncoding)
  139. {
  140. request.GetHeaderParameters()[Constants.ACCEPT_ENCODING] = Constants.CONTENT_ENCODING_GZIP;
  141. }
  142. string realServerUrl = GetServerUrl(this.serverUrl, request.GetApiName(), session);
  143. string reqUrl = WebUtils.BuildRequestUrl(realServerUrl, txtParams);
  144. try
  145. {
  146. string body;
  147. if (request is ITopUploadRequest<T>) // 是否需要上传文件
  148. {
  149. ITopUploadRequest<T> uRequest = (ITopUploadRequest<T>)request;
  150. IDictionary<string, FileItem> fileParams = TopUtils.CleanupDictionary(uRequest.GetFileParameters());
  151. body = webUtils.DoPost(realServerUrl, txtParams, fileParams, request.GetHeaderParameters());
  152. }
  153. else
  154. {
  155. body = webUtils.DoPost(realServerUrl, txtParams, request.GetHeaderParameters());
  156. }
  157. // 解释响应结果
  158. T rsp;
  159. if (disableParser)
  160. {
  161. rsp = Activator.CreateInstance<T>();
  162. rsp.Body = body;
  163. }
  164. else
  165. {
  166. if (Constants.FORMAT_XML.Equals(format))
  167. {
  168. ITopParser<T> tp = new TopXmlParser<T>();
  169. rsp = tp.Parse(body);
  170. }
  171. else
  172. {
  173. ITopParser<T> tp;
  174. if (useSimplifyJson)
  175. {
  176. tp = new TopSimplifyJsonParser<T>();
  177. }
  178. else
  179. {
  180. tp = new TopJsonParser<T>();
  181. }
  182. rsp = tp.Parse(body);
  183. }
  184. }
  185. // 追踪错误的请求
  186. if (rsp.IsError)
  187. {
  188. TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
  189. TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, rsp.Body);
  190. }
  191. return rsp;
  192. }
  193. catch (Exception e)
  194. {
  195. TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
  196. TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
  197. throw e;
  198. }
  199. }
  200. internal virtual string GetServerUrl(string serverUrl, string apiName, string session)
  201. {
  202. return serverUrl;
  203. }
  204. internal virtual string GetSdkVersion()
  205. {
  206. return Constants.SDK_VERSION;
  207. }
  208. internal T CreateErrorResponse<T>(string errCode, string errMsg) where T : TopResponse
  209. {
  210. T rsp = Activator.CreateInstance<T>();
  211. rsp.ErrCode = errCode;
  212. rsp.ErrMsg = errMsg;
  213. if (Constants.FORMAT_XML.Equals(format))
  214. {
  215. XmlDocument root = new XmlDocument();
  216. XmlElement bodyE = root.CreateElement(Constants.ERROR_RESPONSE);
  217. XmlElement codeE = root.CreateElement(Constants.ERROR_CODE);
  218. codeE.InnerText = errCode;
  219. bodyE.AppendChild(codeE);
  220. XmlElement msgE = root.CreateElement(Constants.ERROR_MSG);
  221. msgE.InnerText = errMsg;
  222. bodyE.AppendChild(msgE);
  223. root.AppendChild(bodyE);
  224. rsp.Body = root.OuterXml;
  225. }
  226. else
  227. {
  228. IDictionary<string, object> errObj = new Dictionary<string, object>();
  229. errObj.Add(Constants.ERROR_CODE, errCode);
  230. errObj.Add(Constants.ERROR_MSG, errMsg);
  231. IDictionary<string, object> root = new Dictionary<string, object>();
  232. root.Add(Constants.ERROR_RESPONSE, errObj);
  233. string body = JSON.ToJSON(root);
  234. rsp.Body = body;
  235. }
  236. return rsp;
  237. }
  238. internal void TraceApiError(string appKey, string apiName, string url, Dictionary<string, string> parameters, double latency, string errorMessage)
  239. {
  240. if (!disableTrace)
  241. {
  242. this.topLogger.TraceApiError(appKey, apiName, url, parameters, latency, errorMessage);
  243. }
  244. }
  245. }
  246. }