using Newtonsoft.Json;
|
using RestSharp;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Net;
|
using System.Text;
|
|
namespace Shared.Phone
|
{
|
/// <summary>
|
/// Http访问逻辑
|
/// </summary>
|
public class HdlHttpLogic
|
{
|
//2020.08.04 为什么访问云端弄的那么复杂?我也不知道云端的意义为何
|
//总之,云端就是弄得这么复杂,没办法
|
|
#region ■ 变量声明___________________________
|
|
/// <summary>
|
/// http请求逻辑
|
/// </summary>
|
private static HdlHttpLogic m_Current = null;
|
/// <summary>
|
/// http请求逻辑
|
/// </summary>
|
public static HdlHttpLogic Current
|
{
|
get
|
{
|
if (m_Current == null)
|
{
|
m_Current = new HdlHttpLogic();
|
}
|
return m_Current;
|
}
|
}
|
|
/// <summary>
|
/// 请求地址
|
/// </summary>
|
public string RequestHttpsHost = "https://test-gz.hdlcontrol.com";
|
/// <summary>
|
/// 默认的访问地址(上面的那个会变的)
|
/// </summary>
|
private const string DefultHttpsHost = "https://test-gz.hdlcontrol.com";
|
|
#endregion
|
|
#region ■ Http请求实行_______________________
|
|
/// <summary>
|
/// 请求服务器方法
|
/// </summary>
|
/// <param name="i_RequestName">访问地址</param>
|
/// <param name="i_method">POST 或者 GET 等等</param>
|
/// <param name="i_bodyObj">承载在body里面的类对象</param>
|
/// <param name="i_dicQueryTip">存放文档上标签为【query】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_dicPathTip">存放文档上标签为【path】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_checkMode">当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】</param>
|
/// <param name="i_setAgain">当发送失败时,是否重发,默认不重发</param>
|
/// <param name="i_timeout">超时(秒),默认3秒</param>
|
/// <param name="i_isBasicService">是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加)</param>
|
/// <returns>得到响应的数据</returns>
|
public ResponsePack RequestResponseFromZigbeeHttps(string i_RequestName, Method i_method,
|
object i_bodyObj, Dictionary<string, object> i_dicQueryTip = null, Dictionary<string, object> 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);
|
}
|
|
/// <summary>
|
/// 访问指定接口,并直接返回接口返回的比特,存在错误信息时,返回null
|
/// </summary>
|
/// <returns>返回:并直接返回接口返回的比特,存在错误信息时,返回null</returns>
|
/// <param name="i_RequestName">访问地址</param>
|
/// <param name="i_method">POST 或者 GET 等等</param>
|
/// <param name="i_bodyObj">承载在body里面的类对象</param>
|
/// <param name="i_dicQueryTip">存放文档上标签为【query】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_dicPathTip">存放文档上标签为【path】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_checkMode">当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】</param>
|
/// <param name="i_setAgain">当发送失败时,是否重发,默认不重发</param>
|
/// <param name="i_timeout">超时(秒),默认3秒</param>
|
/// <param name="i_isBasicService">是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加)</param>
|
public byte[] RequestByteFromZigbeeHttps(string i_RequestName, Method i_method,
|
object i_bodyObj, Dictionary<string, object> i_dicQueryTip = null, Dictionary<string, object> 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;
|
}
|
|
/// <summary>
|
/// 私有类型:从新发送(懂的人自然懂,很难解释清楚)
|
/// </summary>
|
/// <param name="i_fullUrl">请求Url的完成路径</param>
|
/// <param name="i_method">POST 或者 GET 等等</param>
|
/// <param name="i_token">token</param>
|
/// <param name="i_bodyObj">承载在body里面的类对象</param>
|
/// <param name="i_dicQueryTip">云端文档上标签为【query】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_dicPathTip">云端文档上标签为【path】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_checkMode">当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】</param>
|
/// <param name="i_timeout">超时(秒)</param>
|
/// <param name="i_isBasicService">是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加)</param>
|
/// <returns></returns>
|
private byte[] RequestByteFromZigbeeHttpsAgain(string i_fullUrl, Method i_method, string i_token,
|
object i_bodyObj, Dictionary<string, object> i_dicQueryTip, Dictionary<string, object> 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;
|
}
|
|
/// <summary>
|
/// 请求服务器方法,指定一个Url,和请求方法,数据,Cookie,得到响应的数据
|
/// </summary>
|
/// <param name="i_requestFullUrl">请求Url的完成路径</param>
|
/// <param name="i_token">token(请勿传入长度为0的字符串,兼顾访问非河东的网址,这里如果为null,代表访问的是非河东的网址)</param>
|
/// <param name="i_method">POST 或者 GET 等等</param>
|
/// <param name="i_bodyObj">承载在body里面的类对象</param>
|
/// <param name="i_dicQueryTip">存放文档上标签为【query】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_dicPathTip">存放文档上标签为【path】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_checkMode">当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】</param>
|
/// <param name="i_timeout">超时(秒)</param>
|
/// <param name="i_isBasicService">是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加)</param>
|
/// <returns>得到响应的数据</returns>
|
public byte[] DoRequestZigbeeHttps(string i_requestFullUrl, string i_token, Method i_method,
|
object i_bodyObj, Dictionary<string, object> i_dicQueryTip, Dictionary<string, object> 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 ■ 第三方接口访问_____________________
|
|
/// <summary>
|
/// 第三方接口访问
|
/// </summary>
|
/// <param name="requestFullUrl">请求Url的完成路径</param>
|
/// <param name="contentType">text/html 或者 application/json</param>
|
/// <param name="requestMethod">POST 或者 GET 等等</param>
|
/// <param name="timeout">超时,默认5秒</param>
|
/// <returns>得到响应的数据</returns>
|
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 ■ 云端访问密匙_______________________
|
|
/// <summary>
|
/// appKey(访问云端固定的东西)
|
/// </summary>
|
private const string APP_KEY = "HDL-HOME-APP-TEST";
|
/// <summary>
|
/// 我也不知道这是什么鬼东西
|
/// </summary>
|
private const string SECRET_KEY = "WeJ8TY88vbakCcnvH8G1tDUqzLWY8yss";
|
/// <summary>
|
/// 获取当前时间戳值(访问云端使用)
|
/// </summary>
|
/// <returns></returns>
|
private string GetTimestamp()
|
{
|
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区
|
return ((long)(DateTime.Now - startTime).TotalSeconds).ToString(); // 相差秒数
|
}
|
|
/// <summary>
|
/// MD5加密
|
/// </summary>
|
/// <param name="signstr"></param>
|
/// <returns></returns>
|
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();
|
}
|
|
/// <summary>
|
/// 基础服务的接口都要校验sign,计算sign签名
|
/// </summary>
|
/// <param name="i_BodyData">body的数据</param>
|
/// <returns></returns>
|
private string GetSignRequestJson(string i_BodyData)
|
{
|
try
|
{
|
//1. 将model实体转为Dictionary<string, object>
|
var paramDictionary = JsonConvert.DeserializeObject<Dictionary<string, object>>(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<string, object> 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______________________
|
|
/// <summary>
|
/// 自动检测Token是否已经过期
|
/// </summary>
|
/// <param name="i_requestFullUrl">请求Url的完成路径</param>
|
/// <param name="i_method">POST 或者 GET 等等</param>
|
/// <param name="i_bodyObj">承载在body里面的类对象</param>
|
/// <param name="i_dicQueryTip">存放文档上标签为【query】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_dicPathTip">存放文档上标签为【path】的变量,无此标签,则请设置为null</param>
|
/// <param name="i_checkMode">当接口需要区分【主账号】和【管理员】时,请选择【A账号权限】</param>
|
/// <param name="i_timeout">超时(秒)</param>
|
/// <param name="i_isBasicService">是否是基础服务的接口(基础服务的接口需要 appKey,timestamp,sign这三个参数,当为true时,内部会自动添加)</param>
|
/// <param name="i_result">接口返回的数据</param>
|
private byte[] CheckTokenIsTimeOut(string i_requestFullUrl, Method i_method,
|
object i_bodyObj, Dictionary<string, object> i_dicQueryTip, Dictionary<string, object> 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 ■ 一般方法___________________________
|
|
/// <summary>
|
/// 获取接口访问的token
|
/// </summary>
|
/// <param name="i_checkMode">是否检测权限</param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 重置云端默认的访问地址
|
/// </summary>
|
public void ResetDefultHttpsHost()
|
{
|
this.RequestHttpsHost = DefultHttpsHost;
|
}
|
|
/// <summary>
|
/// 转换数据
|
/// </summary>
|
/// <param name="i_result">云端访问的byte数据(不是json数据不要调用)</param>
|
/// <returns></returns>
|
public ResponsePack ConvertData(byte[] i_result)
|
{
|
if (i_result == null)
|
{
|
return null;
|
}
|
try
|
{
|
var resultData = Encoding.UTF8.GetString(i_result);
|
var data = JsonConvert.DeserializeObject<ResponsePack>(resultData);
|
return data;
|
}
|
catch { }
|
|
return null;
|
}
|
|
#endregion
|
}
|
}
|