using Newtonsoft.Json; using System.Diagnostics; using System.Net.Http; using System.Text; namespace SHH.CameraDashboard { public static class HttpService { // 单例 HttpClient,避免端口耗尽 private static readonly HttpClient _client; // 【关键】日志事件,UI层订阅这个事件来显示日志 public static event Action OnApiLog; static HttpService() { _client = new HttpClient(); // 设置一个合理的超时,避免界面卡死 _client.Timeout = TimeSpan.FromSeconds(5); } /// /// 泛型 GET 方法 /// public static async Task GetAsync(string url) { return await ExecuteRequestAsync(new HttpRequestMessage(HttpMethod.Get, url)); } /// /// 泛型 POST 方法 /// public static async Task PostAsync(string url, object data) { var request = new HttpRequestMessage(HttpMethod.Post, url); string json = JsonConvert.SerializeObject(data); request.Content = new StringContent(json, Encoding.UTF8, "application/json"); return await ExecuteRequestAsync(request, json); } /// /// 专门用于【连通性测试】的方法 (不关心返回值内容,只关心通不通) /// public static async Task TestConnectionAsync(string url) { // 复用核心逻辑,但泛型传 string (忽略结果) 或 object try { await ExecuteRequestAsync(new HttpRequestMessage(HttpMethod.Get, url)); return true; } catch { return false; } } // --- 核心执行逻辑 --- private static async Task ExecuteRequestAsync(HttpRequestMessage request, string requestBody = "") { // 1. 准备日志对象 var log = new ApiLogEntry { Method = request.Method.ToString(), Url = request.RequestUri.ToString(), RequestBody = requestBody }; var sw = Stopwatch.StartNew(); // 开始计时 try { // 2. 发起网络请求 var response = await _client.SendAsync(request); sw.Stop(); // 停止计时 log.DurationMs = sw.ElapsedMilliseconds; log.StatusCode = (int)response.StatusCode; // 3. 读取响应内容 string content = await response.Content.ReadAsStringAsync(); log.ResponseBody = content; if (response.IsSuccessStatusCode) { // 如果 T 是 string,直接返回内容,不反序列化 if (typeof(T) == typeof(string)) return (T)(object)content; // 反序列化 JSON return JsonConvert.DeserializeObject(content); } else { log.ErrorMessage = $"HTTP {response.StatusCode}: {response.ReasonPhrase}"; throw new HttpRequestException(log.ErrorMessage); } } catch (Exception ex) { sw.Stop(); if (log.DurationMs == 0) log.DurationMs = sw.ElapsedMilliseconds; log.StatusCode = 0; // 0 代表网络层面的失败(如断网) log.ErrorMessage = ex.Message; log.ResponseBody = ex.ToString(); // 记录堆栈以便排查 throw; // 抛出异常供调用方 UI 处理 } finally { // 4. 【广播日志】无论成功失败,都触发事件 // 使用 Invoke 确保 UI 订阅者能收到 OnApiLog?.Invoke(log); } } } }