TapHttpClient.cs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using System.Threading.Tasks;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Net.Http.Headers;
  8. using System.Text;
  9. using Newtonsoft.Json;
  10. using TapSDK.Core.Internal.Json;
  11. using TapSDK.Core.Internal.Log;
  12. namespace TapSDK.Core.Internal.Http {
  13. public class TapHttpClient {
  14. private readonly string clientId;
  15. private readonly string clientToken;
  16. private readonly string serverUrl;
  17. readonly HttpClient client;
  18. private Dictionary<string, Func<Task<string>>> runtimeHeaderTasks = new Dictionary<string, Func<Task<string>>>();
  19. private Dictionary<string, string> additionalHeaders = new Dictionary<string, string>();
  20. public TapHttpClient(string clientID, string clientToken, string serverUrl) {
  21. this.clientId = clientID;
  22. this.clientToken = clientToken;
  23. this.serverUrl = serverUrl;
  24. client = new HttpClient();
  25. client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  26. client.DefaultRequestHeaders.Add("X-LC-Id", clientID);
  27. }
  28. public void AddRuntimeHeaderTask(string key, Func<Task<string>> task) {
  29. if (string.IsNullOrEmpty(key)) {
  30. return;
  31. }
  32. if (task == null) {
  33. return;
  34. }
  35. runtimeHeaderTasks[key] = task;
  36. }
  37. public void AddAddtionalHeader(string key, string value) {
  38. if (string.IsNullOrEmpty(key)) {
  39. return;
  40. }
  41. if (string.IsNullOrEmpty(value)) {
  42. return;
  43. }
  44. additionalHeaders[key] = value;
  45. }
  46. public Task<T> Get<T>(string path,
  47. Dictionary<string, object> headers = null,
  48. Dictionary<string, object> queryParams = null,
  49. bool withAPIVersion = true) {
  50. return Request<T>(path, HttpMethod.Get, headers, null, queryParams, withAPIVersion);
  51. }
  52. public Task<T> Post<T>(string path,
  53. Dictionary<string, object> headers = null,
  54. object data = null,
  55. Dictionary<string, object> queryParams = null,
  56. bool withAPIVersion = true) {
  57. return Request<T>(path, HttpMethod.Post, headers, data, queryParams, withAPIVersion);
  58. }
  59. public Task<T> Put<T>(string path,
  60. Dictionary<string, object> headers = null,
  61. object data = null,
  62. Dictionary<string, object> queryParams = null,
  63. bool withAPIVersion = true) {
  64. return Request<T>(path, HttpMethod.Put, headers, data, queryParams, withAPIVersion);
  65. }
  66. public Task Delete(string path,
  67. Dictionary<string, object> headers = null,
  68. object data = null,
  69. Dictionary<string, object> queryParams = null,
  70. bool withAPIVersion = true) {
  71. return Request<Dictionary<string, object>>(path, HttpMethod.Delete, headers, data, queryParams, withAPIVersion);
  72. }
  73. async Task<T> Request<T>(string path,
  74. HttpMethod method,
  75. Dictionary<string, object> headers = null,
  76. object data = null,
  77. Dictionary<string, object> queryParams = null,
  78. bool withAPIVersion = true) {
  79. string url = BuildUrl(path, queryParams);
  80. HttpRequestMessage request = new HttpRequestMessage {
  81. RequestUri = new Uri(url),
  82. Method = method,
  83. };
  84. await FillHeaders(request.Headers, headers);
  85. string content = null;
  86. if (data != null) {
  87. content = JsonConvert.SerializeObject(data);
  88. StringContent requestContent = new StringContent(content);
  89. requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
  90. request.Content = requestContent;
  91. }
  92. TapHttpUtils.PrintRequest(client, request, content);
  93. HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
  94. request.Dispose();
  95. string resultString = await response.Content.ReadAsStringAsync();
  96. response.Dispose();
  97. TapHttpUtils.PrintResponse(response, resultString);
  98. if (response.IsSuccessStatusCode) {
  99. T ret = JsonConvert.DeserializeObject<T>(resultString,
  100. TapJsonConverter.Default);
  101. return ret;
  102. }
  103. throw HandleErrorResponse(response.StatusCode, resultString);
  104. }
  105. TapException HandleErrorResponse(HttpStatusCode statusCode, string responseContent) {
  106. int code = (int)statusCode;
  107. string message = responseContent;
  108. try {
  109. // 尝试获取 LeanCloud 返回错误信息
  110. Dictionary<string, object> error = JsonConvert.DeserializeObject<Dictionary<string, object>>(responseContent,
  111. TapJsonConverter.Default);
  112. code = (int)error["code"];
  113. message = error["error"].ToString();
  114. } catch (Exception e) {
  115. TapLog.Error(e.Message ?? "");
  116. }
  117. return new TapException(code, message);
  118. }
  119. string BuildUrl(string path, Dictionary<string, object> queryParams) {
  120. string apiServer = serverUrl;
  121. StringBuilder urlSB = new StringBuilder(apiServer.TrimEnd('/'));
  122. urlSB.Append($"/{path}");
  123. string url = urlSB.ToString();
  124. if (queryParams != null) {
  125. IEnumerable<string> queryPairs = queryParams.Select(kv => $"{kv.Key}={kv.Value}");
  126. string queries = string.Join("&", queryPairs);
  127. url = $"{url}?{queries}";
  128. }
  129. return url;
  130. }
  131. async Task FillHeaders(HttpRequestHeaders headers, Dictionary<string, object> reqHeaders = null) {
  132. // 额外 headers
  133. if (reqHeaders != null) {
  134. foreach (KeyValuePair<string, object> kv in reqHeaders) {
  135. headers.Add(kv.Key, kv.Value.ToString());
  136. }
  137. }
  138. if (additionalHeaders.Count > 0) {
  139. foreach (KeyValuePair<string, string> kv in additionalHeaders) {
  140. headers.Add(kv.Key, kv.Value);
  141. }
  142. }
  143. // 服务额外 headers
  144. foreach (KeyValuePair<string, Func<Task<string>>> kv in runtimeHeaderTasks) {
  145. if (headers.Contains(kv.Key)) {
  146. continue;
  147. }
  148. string value = await kv.Value.Invoke();
  149. if (value == null) {
  150. continue;
  151. }
  152. headers.Add(kv.Key, value);
  153. }
  154. }
  155. }
  156. }