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
}
}