AntiAddictionHttpClient.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. using System;
  2. using System.Text;
  3. using System.Linq;
  4. using System.Collections.Generic;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Http.Headers;
  8. using System.Threading.Tasks;
  9. using LC.Newtonsoft.Json;
  10. using TapTap.Common;
  11. using TapTap.Common.Internal.Json;
  12. using TapTap.Common.Internal.Http;
  13. using TapTap.AntiAddiction.Model;
  14. namespace TapTap.AntiAddiction.Internal.Http {
  15. public class AntiAddictionHttpClient {
  16. static readonly int INTERNAL_SERVER_ERROR_LIMIT = 3;
  17. internal readonly string serverUrl;
  18. private readonly HttpClient client;
  19. private readonly Dictionary<string, Func<Task<string>>> runtimeHeaderTasks = new Dictionary<string, Func<Task<string>>>();
  20. private readonly Dictionary<string, string> additionalHeaders = new Dictionary<string, string>();
  21. /// <summary>
  22. /// 服务器错误计数,到达数量后则执行单机模式
  23. /// </summary>
  24. private int internalServerErrorCount;
  25. public AntiAddictionHttpClient(string serverUrl) {
  26. this.serverUrl = serverUrl;
  27. internalServerErrorCount = 0;
  28. client = new HttpClient();
  29. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  30. }
  31. public void AddRuntimeHeaderTask(string key, Func<Task<string>> task) {
  32. if (string.IsNullOrEmpty(key)) {
  33. return;
  34. }
  35. if (task == null) {
  36. return;
  37. }
  38. runtimeHeaderTasks[key] = task;
  39. }
  40. public void ChangeAddtionalHeader(string key, string value) {
  41. if (string.IsNullOrEmpty(key)) {
  42. return;
  43. }
  44. if (string.IsNullOrEmpty(value))
  45. {
  46. if (additionalHeaders.ContainsKey(key))
  47. additionalHeaders.Remove(key);
  48. }
  49. else
  50. {
  51. additionalHeaders[key] = value;
  52. }
  53. }
  54. public Task<T> Get<T>(string path,
  55. Dictionary<string, object> headers = null,
  56. Dictionary<string, object> queryParams = null) {
  57. return Request<T>(path, HttpMethod.Get, headers, null, queryParams);
  58. }
  59. public Task<T> Post<T>(string path,
  60. Dictionary<string, object> headers = null,
  61. object data = null,
  62. Dictionary<string, object> queryParams = null) {
  63. return Request<T>(path, HttpMethod.Post, headers, data, queryParams);
  64. }
  65. public Task<T> Put<T>(string path,
  66. Dictionary<string, object> headers = null,
  67. object data = null,
  68. Dictionary<string, object> queryParams = null) {
  69. return Request<T>(path, HttpMethod.Put, headers, data, queryParams);
  70. }
  71. public Task Delete(string path,
  72. Dictionary<string, object> headers = null,
  73. object data = null,
  74. Dictionary<string, object> queryParams = null) {
  75. return Request<Dictionary<string, object>>(path, HttpMethod.Delete, headers, data, queryParams);
  76. }
  77. async Task<T> Request<T>(string path,
  78. HttpMethod method,
  79. Dictionary<string, object> headers = null,
  80. object data = null,
  81. Dictionary<string, object> queryParams = null) {
  82. bool standaloneEnabled = await TapTapAntiAddictionManager.IsStandaloneEnabled();
  83. if (standaloneEnabled && IsInternalServerErrorLimit) {
  84. throw new Exception("Interval server error.");
  85. }
  86. string url = BuildUrl(path, queryParams);
  87. HttpRequestMessage request = new HttpRequestMessage {
  88. RequestUri = new Uri(url),
  89. Method = method,
  90. };
  91. await FillHeaders(request.Headers, headers);
  92. string content = null;
  93. if (data != null) {
  94. content = JsonConvert.SerializeObject(data);
  95. StringContent requestContent = new StringContent(content);
  96. requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
  97. request.Content = requestContent;
  98. }
  99. TapHttpUtils.PrintRequest(client, request, content);
  100. HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
  101. request.Dispose();
  102. string resultString = await response.Content.ReadAsStringAsync();
  103. response.Dispose();
  104. TapHttpUtils.PrintResponse(response, resultString);
  105. if (response.IsSuccessStatusCode)
  106. {
  107. T ret = JsonConvert.DeserializeObject<T>(resultString,
  108. TapJsonConverter.Default);
  109. return ret;
  110. }
  111. throw HandleErrorResponse(response.StatusCode, resultString);
  112. }
  113. Exception HandleErrorResponse(HttpStatusCode statusCode, string responseContent)
  114. {
  115. if (statusCode == HttpStatusCode.Unauthorized) {
  116. AntiAddictionUIKit.Exit();
  117. return new AntiAddictionException((int)statusCode, responseContent);
  118. }
  119. if (statusCode >= HttpStatusCode.InternalServerError) {
  120. internalServerErrorCount++;
  121. return new AntiAddictionException((int)statusCode, responseContent);
  122. }
  123. int code = (int)statusCode;
  124. string message = responseContent;
  125. string err = null;
  126. string desc = null;
  127. try {
  128. // 尝试获取 LeanCloud 返回错误信息
  129. ErrorResponse error = JsonConvert.DeserializeObject<ErrorResponse>(responseContent,
  130. TapJsonConverter.Default);
  131. code = error.Result.Code;
  132. message = error.Result.Description;
  133. err = error.Result.Error;
  134. desc = error.Result.Description;
  135. } catch (Exception e) {
  136. TapLogger.Error(e);
  137. }
  138. return new AntiAddictionException(code, message) {
  139. Error = err,
  140. Description = desc
  141. };
  142. }
  143. string BuildUrl(string path, Dictionary<string, object> queryParams) {
  144. string apiServer = serverUrl;
  145. StringBuilder urlSB = new StringBuilder(apiServer.TrimEnd('/'));
  146. urlSB.Append($"/{path}");
  147. string url = urlSB.ToString();
  148. if (queryParams != null) {
  149. IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={Uri.EscapeDataString(kv.Value.ToString())}");
  150. string queries = string.Join("&", queryPairs);
  151. url = $"{url}?{queries}";
  152. }
  153. return url;
  154. }
  155. async Task FillHeaders(HttpRequestHeaders headers, Dictionary<string, object> reqHeaders = null) {
  156. // 额外 headers
  157. if (reqHeaders != null) {
  158. foreach (KeyValuePair<string, object> kv in reqHeaders) {
  159. headers.Add(kv.Key, kv.Value.ToString());
  160. }
  161. }
  162. if (additionalHeaders.Count > 0) {
  163. foreach (KeyValuePair<string, string> kv in additionalHeaders) {
  164. headers.Add(kv.Key, kv.Value);
  165. }
  166. }
  167. // 服务额外 headers
  168. foreach (KeyValuePair<string, Func<Task<string>>> kv in runtimeHeaderTasks) {
  169. if (headers.Contains(kv.Key)) {
  170. continue;
  171. }
  172. string value = await kv.Value.Invoke();
  173. if (string.IsNullOrEmpty(value)) {
  174. continue;
  175. }
  176. headers.Add(kv.Key, value);
  177. }
  178. }
  179. bool IsInternalServerErrorLimit => internalServerErrorCount >= INTERNAL_SERVER_ERROR_LIMIT;
  180. }
  181. }