using Newtonsoft.Json; using RestSharp; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; namespace Shared.Phone { /// /// Http访问逻辑 /// public class HdlHttpLogic { //2020.08.04 为什么访问云端弄的那么复杂?我也不知道云端的意义为何 //总之,云端就是弄得这么复杂,没办法 #region ■ 变量声明___________________________ /// /// http请求逻辑 /// private static HdlHttpLogic m_Current = null; /// /// http请求逻辑 /// public static HdlHttpLogic Current { get { if (m_Current == null) { m_Current = new HdlHttpLogic(); } return m_Current; } } /// /// 请求地址 /// public string RequestHttpsHost = "https://test-gz.hdlcontrol.com"; /// /// 默认的访问地址(上面的那个会变的) /// private const string DefultHttpsHost = "https://test-gz.hdlcontrol.com"; #endregion #region ■ Http请求实行_______________________ /// /// 请求服务器方法 /// /// 访问地址 /// POST 或者 GET 等等 /// 承载在body里面的类对象 /// 存放文档上标签为【query】的变量,无此标签,则请设置为null /// 存放文档上标签为【path】的变量,无此标签,则请设置为null /// 当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】 /// 当发送失败时,是否重发,默认不重发 /// 超时(秒),默认3秒 /// 是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加) /// 得到响应的数据 public ResponsePack RequestResponseFromZigbeeHttps(string i_RequestName, Method i_method, object i_bodyObj, Dictionary i_dicQueryTip = null, Dictionary i_dicPathTip = null, CheckMode i_checkMode = CheckMode.A不检测, bool i_setAgain = false, int i_timeout = 3, bool i_isBasicService = false) { //请求Url的完成路径 var result = this.RequestByteFromZigbeeHttps(i_RequestName, i_method, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_setAgain, i_timeout, i_isBasicService); //转换数据为 ResponsePack return this.ConvertData(result); } /// /// 访问指定接口,并直接返回接口返回的比特,存在错误信息时,返回null /// /// 返回:并直接返回接口返回的比特,存在错误信息时,返回null /// 访问地址 /// POST 或者 GET 等等 /// 承载在body里面的类对象 /// 存放文档上标签为【query】的变量,无此标签,则请设置为null /// 存放文档上标签为【path】的变量,无此标签,则请设置为null /// 当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】 /// 当发送失败时,是否重发,默认不重发 /// 超时(秒),默认3秒 /// 是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加) public byte[] RequestByteFromZigbeeHttps(string i_RequestName, Method i_method, object i_bodyObj, Dictionary i_dicQueryTip = null, Dictionary i_dicPathTip = null, CheckMode i_checkMode = CheckMode.A不检测, bool i_setAgain = false, int i_timeout = 3, bool i_isBasicService = false) { //请求Url的完成路径 var fullUrl = RequestHttpsHost + "/" + i_RequestName; //请求token var httpToken = this.GetHttpToken(i_checkMode); //获取从接口那里取到的比特数据 var revertObj = this.DoRequestZigbeeHttps(fullUrl, httpToken, i_method, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_timeout, i_isBasicService); if (revertObj == null) { if (i_setAgain == false) { //当前无法访问网络 return null; } revertObj = RequestByteFromZigbeeHttpsAgain(fullUrl, i_method, httpToken, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_timeout, i_isBasicService); if (revertObj == null) { return null; } } return revertObj; } /// /// 私有类型:从新发送(懂的人自然懂,很难解释清楚) /// /// 请求Url的完成路径 /// POST 或者 GET 等等 /// token /// 承载在body里面的类对象 /// 云端文档上标签为【query】的变量,无此标签,则请设置为null /// 云端文档上标签为【path】的变量,无此标签,则请设置为null /// 当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】 /// 超时(秒) /// 是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加) /// private byte[] RequestByteFromZigbeeHttpsAgain(string i_fullUrl, Method i_method, string i_token, object i_bodyObj, Dictionary i_dicQueryTip, Dictionary i_dicPathTip, CheckMode i_checkMode, int i_timeout, bool i_isBasicService) { byte[] responsePack = null; int count = 0; while (true) { System.Threading.Thread.Sleep(2000); if (HdlCheckLogic.Current.IsAccountLoginOut() == true) { //如果已经退出了登录 return null; } //调用接口 responsePack = this.DoRequestZigbeeHttps(i_fullUrl, i_token, i_method, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_timeout, i_isBasicService); if (responsePack != null) { break; } count++; if (count == 1) { //算了,目前就只重发一次好了 break; } } return responsePack; } /// /// 请求服务器方法,指定一个Url,和请求方法,数据,Cookie,得到响应的数据 /// /// 请求Url的完成路径 /// token(请勿传入长度为0的字符串,兼顾访问非河东的网址,这里如果为null,代表访问的是非河东的网址) /// POST 或者 GET 等等 /// 承载在body里面的类对象 /// 存放文档上标签为【query】的变量,无此标签,则请设置为null /// 存放文档上标签为【path】的变量,无此标签,则请设置为null /// 当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】 /// 超时(秒) /// 是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加) /// 得到响应的数据 public byte[] DoRequestZigbeeHttps(string i_requestFullUrl, string i_token, Method i_method, object i_bodyObj, Dictionary i_dicQueryTip, Dictionary i_dicPathTip, CheckMode i_checkMode, int i_timeout, bool i_isBasicService) { //HttpWebRequest的超时时间,如果设置低于15秒,在没网的情况下,很大几率它的超时时间大于15秒 //所以才这样整 byte[] result = null; bool canBreak = false; new System.Threading.Thread(() => { try { RestClient client = new RestClient(i_requestFullUrl); RestRequest request = new RestRequest(i_method); request.Timeout = i_timeout * 1000; request.AddHeader("content-type", "application/json"); //这个东西是token if (i_token != null) { //Token前面要加上这个东西 request.AddHeader("authorization", Common.Config.Instance.HeaderPrefix + i_token); //固定为2,代表是zigbeeApp request.AddHeader("clientType", "2"); } //加入到Body里面的数据 if (i_bodyObj != null) { if (i_bodyObj is byte[]) { //这个东西应该是文件,不能反序列化,不然数据可能会丢失 request.AddParameter("application/json", i_bodyObj, ParameterType.RequestBody); } else if (i_isBasicService == true) { //访问基础服务接口(为了避免过多的装箱和拆箱,这样整一个else if出来) string strBody = this.GetSignRequestJson(JsonConvert.SerializeObject(i_bodyObj)); request.AddParameter("application/json", strBody, ParameterType.RequestBody); } else { string strBody = JsonConvert.SerializeObject(i_bodyObj); request.AddParameter("application/json", strBody, ParameterType.RequestBody); } } else { if (i_isBasicService == true) { //访问基础服务接口(为了避免过多的装箱和拆箱,这样整一个else if出来) string strBody = this.GetSignRequestJson("{}"); request.AddParameter("application/json", strBody, ParameterType.RequestBody); } } //添加到参数里面的数据 if (i_dicQueryTip != null) { foreach (string dataKey in i_dicQueryTip.Keys) { if (i_dicQueryTip[dataKey] != null) { request.AddQueryParameter(dataKey.Trim(), i_dicQueryTip[dataKey].ToString()); } } } //拼接到地址上的数据 if (i_dicPathTip != null) { foreach (var dataKey in i_dicPathTip.Keys) { if (i_dicPathTip[dataKey] != null) { request.AddUrlSegment(dataKey.Trim(), i_dicPathTip[dataKey].ToString()); } } } IRestResponse response = client.Execute(request); if (response != null && response.StatusCode == HttpStatusCode.OK) { result = response.RawBytes; } canBreak = true; } catch (WebException e) { if (e.Status == WebExceptionStatus.ConnectFailure//由有网络切换到无网络时触发 || e.Status == WebExceptionStatus.Timeout//超时 || e.Status == WebExceptionStatus.NameResolutionFailure)//本身就是无网络时启动时触发 { } canBreak = true; } catch (Exception e) { canBreak = true; } }) { IsBackground = true }.Start(); int timeCount = (i_timeout * 1000) / 20; while (canBreak == false && timeCount > 0) { System.Threading.Thread.Sleep(20); timeCount--; } //自动检测Token是否已经过期 return this.CheckTokenIsTimeOut(i_requestFullUrl, i_method, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_timeout, i_isBasicService, result); } #endregion #region ■ 第三方接口访问_____________________ /// /// 第三方接口访问 /// /// 请求Url的完成路径 /// text/html 或者 application/json /// POST 或者 GET 等等 /// 超时,默认5秒 /// 得到响应的数据 public byte[] RequestThridPartyHttps(string requestFullUrl, string contentType = "text/html", string requestMethod = "GET", int timeout = 5) { try { //初始化新的webRequst //1. 创建httpWebRequest对象 var webRequest = (HttpWebRequest)WebRequest.Create(new Uri(requestFullUrl)); //2. 初始化HttpWebRequest对象 webRequest.Method = requestMethod; webRequest.Timeout = timeout * 1000; //取消使用代理访问 webRequest.Proxy = null; webRequest.UseDefaultCredentials = false; webRequest.ContentType = contentType; //4. 读取服务器的返回信息 var response = (HttpWebResponse)webRequest.GetResponse(); using (var stream = response.GetResponseStream()) { if (stream == null) { return null; } var ms = new System.IO.MemoryStream(); var bytes = new byte[1024]; var len = int.MaxValue; while (stream.CanRead && 0 < len) { len = stream.Read(bytes, 0, bytes.Length); ms.Write(bytes, 0, len); } return ms.ToArray(); } } catch { return null; } } #endregion #region ■ 云端访问密匙_______________________ /// /// appKey(访问云端固定的东西) /// private const string APP_KEY = "HDL-HOME-APP-TEST"; /// /// 我也不知道这是什么鬼东西 /// private const string SECRET_KEY = "WeJ8TY88vbakCcnvH8G1tDUqzLWY8yss"; /// /// 获取当前时间戳值(访问云端使用) /// /// private string GetTimestamp() { System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区 return ((long)(DateTime.Now - startTime).TotalSeconds).ToString(); // 相差秒数 } /// /// MD5加密 /// /// /// private string SignMD5Encrypt(string s) { byte[] sign = System.Security.Cryptography.MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(s)); string signstr = string.Empty; foreach (byte item in sign) { signstr += item.ToString("X2"); } return signstr.ToLower(); } /// /// 基础服务的接口都要校验sign,计算sign签名 /// /// body的数据 /// private string GetSignRequestJson(string i_BodyData) { try { //1. 将model实体转为Dictionary var paramDictionary = JsonConvert.DeserializeObject>(i_BodyData); //2. 计算sign if (paramDictionary != null) { paramDictionary.Add("appKey", APP_KEY); paramDictionary.Add("timestamp", GetTimestamp()); //2.1 字典升序 paramDictionary = paramDictionary.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); //2.2 拼接按URL键值对 string str = string.Empty; foreach (KeyValuePair item in paramDictionary) { if (item.Value == null) { //不校验 continue; } string myValue = item.Value.ToString(); if (myValue == string.Empty) { //空字符也不校验 continue; } if (item.Value is bool) { //云端那帮沙雕说bool类型必须用小写 myValue = myValue.ToLower(); } str += item.Key + "=" + myValue + "&"; } //2.3 拼接SECRET_KEY str = str.Substring(0, str.Length - 1) + SECRET_KEY; //2.4 MD5转换+转小写 var signstr = SignMD5Encrypt(str); paramDictionary.Add("sign", signstr); return JsonConvert.SerializeObject(paramDictionary); } else { return ""; } } catch { return ""; } } #endregion #region ■ 自动检测Token______________________ /// /// 自动检测Token是否已经过期 /// /// 请求Url的完成路径 /// POST 或者 GET 等等 /// 承载在body里面的类对象 /// 存放文档上标签为【query】的变量,无此标签,则请设置为null /// 存放文档上标签为【path】的变量,无此标签,则请设置为null /// 当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】 /// 超时(秒) /// 是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加) /// 接口返回的数据 private byte[] CheckTokenIsTimeOut(string i_requestFullUrl, Method i_method, object i_bodyObj, Dictionary i_dicQueryTip, Dictionary i_dicPathTip, CheckMode i_checkMode, int i_timeout, bool i_isBasicService, byte[] i_result) { if (i_result == null || i_result.Length == 0 || i_result[0] != '{' || i_result[i_result.Length - 1] != '}') { //以上都不是正规的json return i_result; } var result = this.ConvertData(i_result); if (result == null) { return i_result; } if (result.Code == HttpMessageEnum.A繁忙 || result.Code == HttpMessageEnum.A超时) { HdlMessageLogic.Current.ShowNetCodeTipMsg(ShowNetCodeMode.YES, result); return null; } if (result.Code == HttpMessageEnum.A40001 //这个是token过期 || result.Code == HttpMessageEnum.A10006) //这个是token不对劲 { bool seccess = false; if (i_checkMode == CheckMode.A账号权限) { //重新刷新主人的token seccess = HdlAccountLogic.Current.InitMasterToken(); } else { //这个是token过期 var reponse = HdlAccountLogic.Current.LoginByRefreshToken(); if (reponse == null || reponse.Code != HttpMessageEnum.A成功) { seccess = false; } } //如果刷新token失败,则重新登陆 if (seccess == false) { HdlThreadLogic.Current.RunMain(() => { //登陆Token已经过期,请重新登陆 string msg = Language.StringByID(R.MyInternationalizationString.uLoginTokenIsTimeOutPleaseLoginAgain); HdlMessageLogic.Current.ShowMassage(ShowMsgType.Tip, msg); HdlAccountLogic.Current.ReLoginAgain(HdlUserCenterResourse.UserInfo.Account); }); return null; } //重新调用接口 var httpToken = this.GetHttpToken(i_checkMode); return this.DoRequestZigbeeHttps(i_requestFullUrl, Common.Config.Instance.Token, i_method, i_bodyObj, i_dicQueryTip, i_dicPathTip, i_checkMode, i_timeout, i_isBasicService); } return i_result; } #endregion #region ■ 一般方法___________________________ /// /// 获取接口访问的token /// /// 是否检测权限 /// public string GetHttpToken(CheckMode i_checkMode) { if (i_checkMode == CheckMode.A不检测) { return Common.Config.Instance.Token; } if (HdlUserCenterResourse.ResidenceOption.AuthorityNo == 2) { //管理员则使用主人的token return Common.Config.Instance.MasterToken; } return Common.Config.Instance.Token; } /// /// 重置云端默认的访问地址 /// public void ResetDefultHttpsHost() { this.RequestHttpsHost = DefultHttpsHost; } /// /// 转换数据 /// /// 云端访问的byte数据(不是json数据不要调用) /// public ResponsePack ConvertData(byte[] i_result) { if (i_result == null) { return null; } try { var resultData = Encoding.UTF8.GetString(i_result); var data = JsonConvert.DeserializeObject(resultData); return data; } catch { } return null; } #endregion } }