using System; using System.Collections.Generic; using System.Text; using System.Xml; using QimenCloud.Api.Parser; using Top.Api; using Top.Api.Parser; using Top.Api.Util; using FastJSON; using System.Collections; using XmlWriter = Top.Api.Util.XmlWriter; namespace QimenCloud.Api { public class DefaultQimenCloudClient : IQimenCloudClient { internal string serverUrl; internal string appKey; internal string appSecret; internal string format = Constants.FORMAT_JSON; internal string signMethod = Constants.SIGN_METHOD_HMAC_SHA256; internal WebUtils webUtils; internal ITopLogger topLogger; internal bool disableParser = false; // 禁用响应结果解释 internal bool disableTrace = false; // 禁用日志调试功能 internal bool useGzipEncoding = true; // 是否启用响应GZIP压缩 internal IDictionary systemParameters; // 设置所有请求共享的系统级参数 #region DefaultQimenCloudClient Constructors public DefaultQimenCloudClient(string serverUrl, string appKey, string appSecret) { this.appKey = appKey; this.appSecret = appSecret; this.serverUrl = serverUrl; this.webUtils = new WebUtils(); this.topLogger = Log.Instance; } public DefaultQimenCloudClient(string serverUrl, string appKey, string appSecret, string format) : this(serverUrl, appKey, appSecret) { this.format = format; } #endregion public void SetTimeout(int timeout) { this.webUtils.Timeout = timeout; } public void SetReadWriteTimeout(int readWriteTimeout) { this.webUtils.ReadWriteTimeout = readWriteTimeout; } public void SetSignMethod(String signMethod) { this.signMethod = signMethod; } public void SetDisableParser(bool disableParser) { this.disableParser = disableParser; } public void SetDisableTrace(bool disableTrace) { this.disableTrace = disableTrace; } public void SetUseGzipEncoding(bool useGzipEncoding) { this.useGzipEncoding = useGzipEncoding; } public void SetIgnoreSSLCheck(bool ignore) { this.webUtils.IgnoreSSLCheck = ignore; } public void SetSystemParameters(IDictionary systemParameters) { this.systemParameters = systemParameters; } #region ITopClient Members public virtual T Execute(IQimenCloudRequest request) where T : QimenCloudResponse { return DoExecute(request, null, DateTime.Now); } public virtual T Execute(IQimenCloudRequest request, string session) where T : QimenCloudResponse { return DoExecute(request, session, DateTime.Now); } public virtual T Execute(IQimenCloudRequest request, string session, DateTime timestamp) where T : QimenCloudResponse { return DoExecute(request, session, timestamp); } #endregion private T DoExecute(IQimenCloudRequest request, string session, DateTime timestamp) where T : QimenCloudResponse { long start = DateTime.Now.Ticks; // 提前检查业务参数 try { request.Validate(); } catch (TopException e) { return CreateErrorResponse(e.ErrorCode, e.ErrorMsg); } // 兼容 老奇门1.0 的 XML BODY String apiBody = null; Object[] requestXmlBodyType = request.GetType().GetCustomAttributes(typeof(RequestXmlBodyAttribute), false); Boolean isXmlBody = (requestXmlBodyType != null && requestXmlBodyType.Length > 0); if (isXmlBody) { XmlWriter writer = new XmlWriter(Constants.QM_ROOT_TAG_REQ, typeof(IQimenCloudRequest)); apiBody = writer.Write(request); } // 添加协议级请求参数 TopDictionary txtParams = new TopDictionary(request.GetParameters()); txtParams.Add(Constants.METHOD, request.GetApiName()); txtParams.Add(Constants.SIGN_METHOD, signMethod); txtParams.Add(Constants.APP_KEY, appKey); txtParams.Add(Constants.FORMAT, format); txtParams.Add(Constants.VERSION, "2.0"); txtParams.Add(Constants.PARTNER_ID, GetSdkVersion()); txtParams.Add(Constants.TIMESTAMP, timestamp); txtParams.Add(Constants.TARGET_APP_KEY, request.GetTargetAppKey()); txtParams.Add(Constants.SESSION, session); txtParams.AddAll(this.systemParameters); // 添加签名参数 if (isXmlBody) { txtParams.Add(Constants.SIGN, TopUtils.SignTopRequest(txtParams, apiBody, appSecret, signMethod)); } else { txtParams.Add(Constants.SIGN, TopUtils.SignTopRequest(txtParams, appSecret, signMethod)); } // 添加头部参数 if (this.useGzipEncoding) { request.GetHeaderParameters()[Constants.ACCEPT_ENCODING] = Constants.CONTENT_ENCODING_GZIP; } string realServerUrl = GetServerUrl(this.serverUrl, request.GetApiName(), session); string reqUrl; if (isXmlBody) { reqUrl = WebUtils.BuildRequestUrl(realServerUrl, txtParams); } else { reqUrl = WebUtils.BuildRequestUrl(realServerUrl, txtParams); } try { string body; if (isXmlBody) { body = webUtils.DoPost(reqUrl, Encoding.UTF8.GetBytes(apiBody), Constants.QM_CONTENT_TYPE, request.GetHeaderParameters()); } else { body = webUtils.DoPost(realServerUrl, txtParams, request.GetHeaderParameters()); } // 解释响应结果 T rsp; if (disableParser) { rsp = Activator.CreateInstance(); rsp.Body = body; rsp.RequestUrl = reqUrl; } else { if (Constants.FORMAT_XML.Equals(format)) { ITopParser tp = new QimenCloudXmlParser(); rsp = tp.Parse(body); } else { ITopParser tp = new QimenCloudSimplifyJsonParser(); rsp = tp.Parse(body); } rsp.RequestUrl = reqUrl; } // 追踪错误的请求 if (rsp.IsError) { TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start); TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, rsp.Body); } return rsp; } catch (Exception e) { TimeSpan latency = new TimeSpan(DateTime.Now.Ticks - start); TraceApiError(appKey, request.GetApiName(), serverUrl, txtParams, latency.TotalMilliseconds, e.GetType() + ": " + e.Message); throw e; } } internal virtual string GetServerUrl(string serverUrl, string apiName, string session) { return serverUrl; } internal virtual string GetSdkVersion() { return Constants.SDK_VERSION; } internal T CreateErrorResponse(string errCode, string errMsg) where T : QimenCloudResponse { T rsp = Activator.CreateInstance(); rsp.Code = errCode; rsp.Message = errMsg; if (Constants.FORMAT_XML.Equals(format)) { XmlDocument root = new XmlDocument(); XmlElement bodyE = root.CreateElement(Constants.ERROR_RESPONSE); XmlElement codeE = root.CreateElement(Constants.ERROR_CODE); codeE.InnerText = errCode; bodyE.AppendChild(codeE); XmlElement msgE = root.CreateElement(Constants.ERROR_MSG); msgE.InnerText = errMsg; bodyE.AppendChild(msgE); root.AppendChild(bodyE); rsp.Body = root.OuterXml; } else { IDictionary errObj = new Dictionary(); errObj.Add(Constants.ERROR_CODE, errCode); errObj.Add(Constants.ERROR_MSG, errMsg); IDictionary root = new Dictionary(); root.Add(Constants.ERROR_RESPONSE, errObj); string body = JSON.ToJSON(root); rsp.Body = body; } return rsp; } internal void TraceApiError(string appKey, string apiName, string url, Dictionary parameters, double latency, string errorMessage) { if (!disableTrace) { this.topLogger.TraceApiError(appKey, apiName, url, parameters, latency, errorMessage); } } } }