| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Top.Api.Parser;
- using Top.Api.Util;
- namespace Top.Api
- {
- public class BatchTopClient : DefaultTopClient
- {
- private const string BATCH_API_HEADER_SPLIT = "top-api-separator"; // 批量API用户自定义分隔符Header Key
- private const string BATCH_API_PUBLIC_PARAMETER = "#PUBLIC#"; // 批量API公共参数头
- private const string BATCH_API_DEFAULT_SPLIT = "\r\n-S-\r\n";// 批量API默认分隔符
- private const string BATCH_API_CONTENT_TYPE = "text/plain;charset=utf-8";// 批量API请求文档类型
- private string batchServerUrl;
- private string batchApiSeparator; // 自定义批量API分隔符
- public BatchTopClient(string serverUrl, string appKey, string appSecret)
- : base(BuildApiServerUrl(serverUrl), appKey, appSecret)
- {
- this.batchServerUrl = serverUrl;
- }
- public BatchTopClient(string serverUrl, string appKey, string appSecret, string format)
- : base(BuildApiServerUrl(serverUrl), appKey, appSecret, format)
- {
- this.batchServerUrl = serverUrl;
- }
- public void SetBatchApiSeparator(string batchApiSeparator)
- {
- this.batchApiSeparator = batchApiSeparator;
- }
- public override T Execute<T>(ITopRequest<T> request)
- {
- return Execute<T>(request, null);
- }
- public override T Execute<T>(ITopRequest<T> request, string session)
- {
- return Execute<T>(request, session, DateTime.Now);
- }
- public override T Execute<T>(ITopRequest<T> request, string session, DateTime timestamp)
- {
- if (typeof(TopBatchRequest) == request.GetType())
- {
- return DoExecute<T>(request, session, timestamp);
- }
- else
- {
- return base.Execute<T>(request, session, timestamp);
- }
- }
- private T DoExecute<T>(ITopRequest<T> request, string session, DateTime timestamp) where T : TopResponse
- {
- long start = DateTime.Now.Ticks;
- TopBatchRequest batchRequest = request as TopBatchRequest;
- List<ITopRequest<TopResponse>> requestList = batchRequest.RequestList;
- if (requestList == null || requestList.Count == 0)
- {
- throw new TopException("40", "client-error:api request list is empty");
- }
- // 本地校验请求参数
- if (batchRequest.PublicParams == null || batchRequest.PublicParams.Count == 0)
- {
- for (int i = 0; i < requestList.Count; i++)
- {
- try
- {
- requestList[i].Validate();
- }
- catch (TopException e)
- {
- return CreateErrorResponse<T>(e.ErrorCode, e.ErrorMsg);
- }
- }
- }
- // 添加协议级请求参数
- TopDictionary parameters = new TopDictionary();
- parameters.Add(Constants.VERSION, "2.0");
- parameters.Add(Constants.APP_KEY, appKey);
- parameters.Add(Constants.TIMESTAMP, timestamp);
- parameters.Add(Constants.FORMAT, format);
- parameters.Add(Constants.SIGN_METHOD, Constants.SIGN_METHOD_HMAC);
- parameters.Add(Constants.PARTNER_ID, GetSdkVersion());
- parameters.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey());
- parameters.Add(Constants.SESSION, session);
- if (Constants.FORMAT_JSON.Equals(format) && this.useSimplifyJson)
- {
- parameters.Add(Constants.SIMPLIFY, "true");
- }
- // 添加自定义分隔符
- string separator = BATCH_API_DEFAULT_SPLIT;
- if (!string.IsNullOrEmpty(batchApiSeparator))
- {
- batchRequest.AddHeaderParameter(BATCH_API_HEADER_SPLIT, separator = batchApiSeparator);
- }
- // 是否需要压缩响应
- if (this.useGzipEncoding)
- {
- batchRequest.AddHeaderParameter(Constants.ACCEPT_ENCODING, Constants.CONTENT_ENCODING_GZIP);
- }
- try
- {
- // 添加公共请求头
- if (!string.IsNullOrEmpty(batchRequest.PublicMethod))
- {
- batchRequest.AddPublicParam(Constants.METHOD, batchRequest.PublicMethod);
- }
- else
- {
- if (IsSameRequest(requestList))
- {
- batchRequest.AddPublicParam(Constants.METHOD, requestList[0].GetApiName());
- }
- }
- // 构建批量请求主体
- StringBuilder requestBody = new StringBuilder();
- string publicParamStr = WebUtils.BuildQuery(batchRequest.PublicParams);
- if (!string.IsNullOrEmpty(publicParamStr))
- {
- requestBody.Append(BATCH_API_PUBLIC_PARAMETER).Append(publicParamStr).Append(separator);
- }
- // 组装每个API的请求参数
- for (int i = 0; i < requestList.Count; i++)
- {
- ITopRequest<TopResponse> bRequest = requestList[i];
- bRequest.SetBatchApiOrder(i);
- IDictionary<string, string> apiParams = bRequest.GetParameters();
- // 如果单个API的方法和批量API的公共方法不一致,那么需要设置单个API的方法名称
- if (!string.IsNullOrEmpty(bRequest.GetApiName()) && !bRequest.GetApiName().Equals(batchRequest.PublicMethod))
- {
- apiParams.Add(Constants.METHOD, bRequest.GetApiName());
- }
- if (!string.IsNullOrEmpty(request.GetBatchApiSession()))
- {
- apiParams.Add(Constants.SESSION, bRequest.GetBatchApiSession());
- }
- if (!string.IsNullOrEmpty(request.GetTargetAppKey()))
- {
- apiParams.Add(Constants.TARGET_APP_KEY, bRequest.GetTargetAppKey());
- }
- string apiParamStr = WebUtils.BuildQuery(apiParams);
- if (string.IsNullOrEmpty(apiParamStr))
- {
- apiParamStr = "N";
- }
- requestBody.Append(apiParamStr);
- if (i != requestList.Count - 1)
- {
- requestBody.Append(separator);
- }
- }
- string apiBody = requestBody.ToString();
- // 添加签名参数
- parameters.Add(Constants.SIGN, TopUtils.SignTopRequest(parameters, apiBody, appSecret, Constants.SIGN_METHOD_HMAC));
- // 发起批量请求
- string fullUrl = WebUtils.BuildRequestUrl(this.batchServerUrl, parameters);
- string rsp = webUtils.DoPost(fullUrl, Encoding.UTF8.GetBytes(apiBody), BATCH_API_CONTENT_TYPE, batchRequest.GetHeaderParameters());
- // 构造响应解释器
- ITopParser<TopResponse> parser = null;
- if (Constants.FORMAT_XML.Equals(format))
- {
- parser = new TopXmlParser<TopResponse>();
- }
- else
- {
- if (this.useSimplifyJson)
- {
- parser = new TopSimplifyJsonParser<TopResponse>();
- }
- else
- {
- parser = new TopJsonParser<TopResponse>();
- }
- }
- // 解释响应结果
- TopBatchResponse batchResponse = new TopBatchResponse();
- batchResponse.Body = rsp;
- string[] responseArray = batchResponse.Body.Split(new string[] { separator }, StringSplitOptions.None);
- // 批量API在走单通道验证时没通过,如前面验证,此时只有一个报错信息
- if (responseArray.Length > 0 && responseArray.Length != requestList.Count)
- {
- TopResponse tRsp = parser.Parse(responseArray[0], requestList[0].GetType().BaseType.GetGenericArguments()[0]);
- batchResponse.ErrCode = tRsp.ErrCode;
- batchResponse.ErrMsg = tRsp.ErrMsg;
- batchResponse.SubErrCode = tRsp.SubErrCode;
- batchResponse.SubErrMsg = tRsp.SubErrMsg;
- }
- else
- {
- for (int i = 0; i < responseArray.Length; i++)
- {
- TopResponse tRsp = parser.Parse(responseArray[i], requestList[i].GetType().BaseType.GetGenericArguments()[0]);
- tRsp.Body = responseArray[i];
- batchResponse.AddResponse(tRsp);
- }
- }
- if (batchResponse.IsError)
- {
- TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
- TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, batchResponse.Body);
- }
- return batchResponse as T;
- }
- catch (Exception e)
- {
- TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start);
- TraceApiError(appKey, "BatchApi", batchServerUrl, parameters, latency.TotalMilliseconds, e.GetType() + ": " + e.Message);
- throw e;
- }
- }
- private bool IsSameRequest<T>(List<ITopRequest<T>> requestList) where T : TopResponse
- {
- if (requestList != null && requestList.Count > 1) // 只有两个或以上的请求才考虑合并
- {
- string firstMethod = requestList[0].GetApiName();
- for (int i = 1; i < requestList.Count; i++)
- {
- string currentMethod = requestList[i].GetApiName();
- if (!firstMethod.Equals(currentMethod))
- {
- return false;
- }
- }
- }
- return true;
- }
- private static string BuildApiServerUrl(string batchServerUrl)
- {
- if (batchServerUrl.Contains("/router/batch"))
- {
- return batchServerUrl.Replace("/router/batch", "/router/rest");
- }
- return batchServerUrl;
- }
- }
- }
|