BatchTopClient.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Top.Api.Parser;
  5. using Top.Api.Util;
  6. namespace Top.Api
  7. {
  8. public class BatchTopClient : DefaultTopClient
  9. {
  10. private const string BATCH_API_HEADER_SPLIT = "top-api-separator"; // 批量API用户自定义分隔符Header Key
  11. private const string BATCH_API_PUBLIC_PARAMETER = "#PUBLIC#"; // 批量API公共参数头
  12. private const string BATCH_API_DEFAULT_SPLIT = "\r\n-S-\r\n";// 批量API默认分隔符
  13. private const string BATCH_API_CONTENT_TYPE = "text/plain;charset=utf-8";// 批量API请求文档类型
  14. private string batchServerUrl;
  15. private string batchApiSeparator; // 自定义批量API分隔符
  16. public BatchTopClient(string serverUrl, string appKey, string appSecret)
  17. : base(BuildApiServerUrl(serverUrl), appKey, appSecret)
  18. {
  19. this.batchServerUrl = serverUrl;
  20. }
  21. public BatchTopClient(string serverUrl, string appKey, string appSecret, string format)
  22. : base(BuildApiServerUrl(serverUrl), appKey, appSecret, format)
  23. {
  24. this.batchServerUrl = serverUrl;
  25. }
  26. public void SetBatchApiSeparator(string batchApiSeparator)
  27. {
  28. this.batchApiSeparator = batchApiSeparator;
  29. }
  30. public override T Execute<T>(ITopRequest<T> request)
  31. {
  32. return Execute<T>(request, null);
  33. }
  34. public override T Execute<T>(ITopRequest<T> request, string session)
  35. {
  36. return Execute<T>(request, session, DateTime.Now);
  37. }
  38. public override T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp)
  39. {
  40. if (typeof(TopBatchRequest) == request.GetType())
  41. {
  42. return DoExecute<T>(request, session, timestamp);
  43. }
  44. else
  45. {
  46. return base.Execute<T>(request, session, timestamp);
  47. }
  48. }
  49. private T DoExecute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
  50. {
  51. long start = DateTime.Now.Ticks;
  52. TopBatchRequest batchRequest = request as TopBatchRequest;
  53. List<ITopRequest<TopResponse>> requestList = batchRequest.RequestList;
  54. if (requestList == null || requestList.Count == 0)
  55. {
  56. throw new TopException("40", "client-error:api request list is empty");
  57. }
  58. // 本地校验请求参数
  59. if (batchRequest.PublicParams == null || batchRequest.PublicParams.Count == 0)
  60. {
  61. for (int i = 0; i < requestList.Count; i++)
  62. {
  63. try
  64. {
  65. requestList[i].Validate();
  66. }
  67. catch (TopException e)
  68. {
  69. return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
  70. }
  71. }
  72. }
  73. // 添加协议级请求参数
  74. TopDictionary parameters = new TopDictionary();
  75. parameters.Add(Constants.VERSION, "2.0");
  76. parameters.Add(Constants.APP_KEY, appKey);
  77. parameters.Add(Constants.TIMESTAMP, timestamp);
  78. parameters.Add(Constants.FORMAT, format);
  79. parameters.Add(Constants.SIGN_METHOD, Constants.SIGN_METHOD_HMAC);
  80. parameters.Add(Constants.PARTNER_ID, GetSdkVersion());
  81. parameters.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey());
  82. parameters.Add(Constants.SESSION, session);
  83. if (Constants.FORMAT_JSON.Equals(format) && this.useSimplifyJson)
  84. {
  85. parameters.Add(Constants.SIMPLIFY, "true");
  86. }
  87. // 添加自定义分隔符
  88. string separator = BATCH_API_DEFAULT_SPLIT;
  89. if (!string.IsNullOrEmpty(batchApiSeparator))
  90. {
  91. batchRequest.AddHeaderParameter(BATCH_API_HEADER_SPLIT, separator = batchApiSeparator);
  92. }
  93. // 是否需要压缩响应
  94. if (this.useGzipEncoding)
  95. {
  96. batchRequest.AddHeaderParameter(Constants.ACCEPT_ENCODING, Constants.CONTENT_ENCODING_GZIP);
  97. }
  98. try
  99. {
  100. // 添加公共请求头
  101. if (!string.IsNullOrEmpty(batchRequest.PublicMethod))
  102. {
  103. batchRequest.AddPublicParam(Constants.METHOD, batchRequest.PublicMethod);
  104. }
  105. else
  106. {
  107. if (IsSameRequest(requestList))
  108. {
  109. batchRequest.AddPublicParam(Constants.METHOD, requestList[0].GetApiName());
  110. }
  111. }
  112. // 构建批量请求主体
  113. StringBuilder requestBody = new StringBuilder();
  114. string publicParamStr = WebUtils.BuildQuery(batchRequest.PublicParams);
  115. if (!string.IsNullOrEmpty(publicParamStr))
  116. {
  117. requestBody.Append(BATCH_API_PUBLIC_PARAMETER).Append(publicParamStr).Append(separator);
  118. }
  119. // 组装每个API的请求参数
  120. for (int i = 0; i < requestList.Count; i++)
  121. {
  122. ITopRequest<TopResponse> bRequest = requestList[i];
  123. bRequest.SetBatchApiOrder(i);
  124. IDictionary<string, string> apiParams = bRequest.GetParameters();
  125. // 如果单个API的方法和批量API的公共方法不一致,那么需要设置单个API的方法名称
  126. if (!string.IsNullOrEmpty(bRequest.GetApiName()) && !bRequest.GetApiName().Equals(batchRequest.PublicMethod))
  127. {
  128. apiParams.Add(Constants.METHOD, bRequest.GetApiName());
  129. }
  130. if (!string.IsNullOrEmpty(request.GetBatchApiSession()))
  131. {
  132. apiParams.Add(Constants.SESSION, bRequest.GetBatchApiSession());
  133. }
  134. if (!string.IsNullOrEmpty(request.GetTargetAppKey()))
  135. {
  136. apiParams.Add(Constants.TARGET_APP_KEY, bRequest.GetTargetAppKey());
  137. }
  138. string apiParamStr = WebUtils.BuildQuery(apiParams);
  139. if (string.IsNullOrEmpty(apiParamStr))
  140. {
  141. apiParamStr = "N";
  142. }
  143. requestBody.Append(apiParamStr);
  144. if (i != requestList.Count - 1)
  145. {
  146. requestBody.Append(separator);
  147. }
  148. }
  149. string apiBody = requestBody.ToString();
  150. // 添加签名参数
  151. parameters.Add(Constants.SIGN, TopUtils.SignTopRequest(parameters, apiBody, appSecret, Constants.SIGN_METHOD_HMAC));
  152. // 发起批量请求
  153. string fullUrl = WebUtils.BuildRequestUrl(this.batchServerUrl, parameters);
  154. string rsp = webUtils.DoPost(fullUrl, Encoding.UTF8.GetBytes(apiBody), BATCH_API_CONTENT_TYPE, batchRequest.GetHeaderParameters());
  155. // 构造响应解释器
  156. ITopParser<TopResponse> parser = null;
  157. if (Constants.FORMAT_XML.Equals(format))
  158. {
  159. parser = new TopXmlParser<TopResponse>();
  160. }
  161. else
  162. {
  163. if (this.useSimplifyJson)
  164. {
  165. parser = new TopSimplifyJsonParser<TopResponse>();
  166. }
  167. else
  168. {
  169. parser = new TopJsonParser<TopResponse>();
  170. }
  171. }
  172. // 解释响应结果
  173. TopBatchResponse batchResponse = new TopBatchResponse();
  174. batchResponse.Body = rsp;
  175. string[] responseArray = batchResponse.Body.Split(new string[] { separator }, StringSplitOptions.None);
  176. // 批量API在走单通道验证时没通过,如前面验证,此时只有一个报错信息
  177. if (responseArray.Length > 0 && responseArray.Length != requestList.Count)
  178. {
  179. TopResponse tRsp = parser.Parse(responseArray[0], requestList[0].GetType().BaseType.GetGenericArguments()[0]);
  180. batchResponse.ErrCode = tRsp.ErrCode;
  181. batchResponse.ErrMsg = tRsp.ErrMsg;
  182. batchResponse.SubErrCode = tRsp.SubErrCode;
  183. batchResponse.SubErrMsg = tRsp.SubErrMsg;
  184. }
  185. else
  186. {
  187. for (int i = 0; i < responseArray.Length; i++)
  188. {
  189. TopResponse tRsp = parser.Parse(responseArray[i], requestList[i].GetType().BaseType.GetGenericArguments()[0]);
  190. tRsp.Body = responseArray[i];
  191. batchResponse.AddResponse(tRsp);
  192. }
  193. }
  194. if (batchResponse.IsError)
  195. {
  196. TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
  197. TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, batchResponse.Body);
  198. }
  199. return batchResponse as T;
  200. }
  201. catch (Exception e)
  202. {
  203. TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
  204. TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
  205. throw e;
  206. }
  207. }
  208. private bool IsSameRequest<T>(List<ITopRequest<T>> requestList) where T : TopResponse
  209. {
  210. if (requestList != null && requestList.Count > 1) // 只有两个或以上的请求才考虑合并
  211. {
  212. string firstMethod = requestList[0].GetApiName();
  213. for (int i = 1; i < requestList.Count; i++)
  214. {
  215. string currentMethod = requestList[i].GetApiName();
  216. if (!firstMethod.Equals(currentMethod))
  217. {
  218. return false;
  219. }
  220. }
  221. }
  222. return true;
  223. }
  224. private static string BuildApiServerUrl(string batchServerUrl)
  225. {
  226. if (batchServerUrl.Contains("/router/batch"))
  227. {
  228. return batchServerUrl.Replace("/router/batch", "/router/rest");
  229. }
  230. return batchServerUrl;
  231. }
  232. }
  233. }