using System.Diagnostics; using System.Net; using System.Text; using System.Text.Json; namespace XFEExtension.NetCore.ServerInteractive.Utilities.Helpers; /// 服务器交互帮助类。 public static class InteractiveHelper { private const int DefaultMaxResponseBytes = 8 * 1024 * 1024; private static readonly HttpClient s_client = new() { Timeout = Timeout.InfiniteTimeSpan }; public static Task<(string, HttpStatusCode)> GetServerResponse(string requestAddress, string postBody) => GetServerResponse(new Uri(requestAddress, UriKind.Absolute), postBody, CancellationToken.None); public static async Task<(string, HttpStatusCode)> GetServerResponse(Uri requestAddress, string postBody, CancellationToken cancellationToken, TimeSpan? timeout = null, int maxResponseBytes = DefaultMaxResponseBytes) { using var timeoutSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeoutSource.CancelAfter(timeout ?? TimeSpan.FromSeconds(30)); using var content = new StringContent(postBody, Encoding.UTF8, "application/json"); using var response = await s_client.PostAsync(requestAddress, content, timeoutSource.Token).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(timeoutSource.Token).ConfigureAwait(false); using var output = new MemoryStream(); var buffer = new byte[16 * 1024]; while (true) { var count = await stream.ReadAsync(buffer, timeoutSource.Token).ConfigureAwait(false); if (count == 0) break; if (output.Length + count > maxResponseBytes) throw new HttpRequestException($"服务器响应超过 {maxResponseBytes} 字节限制"); await output.WriteAsync(buffer.AsMemory(0, count), timeoutSource.Token).ConfigureAwait(false); } return (Encoding.UTF8.GetString(output.GetBuffer(), 0, checked((int)output.Length)), response.StatusCode); } public static Task<(string, HttpStatusCode)> GetServerResponse(string requestAddress, object postBody, JsonSerializerOptions jsonSerializerOptions) => GetServerResponse(new Uri(requestAddress, UriKind.Absolute), JsonSerializer.Serialize(postBody, jsonSerializerOptions), CancellationToken.None); public static Task<(string, HttpStatusCode)> GetServerResponse(Uri requestAddress, object postBody, JsonSerializerOptions jsonSerializerOptions, CancellationToken cancellationToken) => GetServerResponse(requestAddress, JsonSerializer.Serialize(postBody, jsonSerializerOptions), cancellationToken); public static Uri BuildRequestUri(string baseAddress, string route) { ArgumentException.ThrowIfNullOrWhiteSpace(baseAddress); ArgumentException.ThrowIfNullOrWhiteSpace(route); var normalizedBase = baseAddress.EndsWith('/') ? baseAddress : $"{baseAddress}/"; return new Uri(new Uri(normalizedBase, UriKind.Absolute), route.TrimStart('/')); } public static string GetStopWatchTime(Stopwatch stopwatch) { if (stopwatch.Elapsed.TotalSeconds > 1) return $"{stopwatch.Elapsed.TotalSeconds:F1} s"; if (stopwatch.Elapsed.TotalMilliseconds > 1) return $"{stopwatch.Elapsed.TotalMilliseconds:F1} ms"; return stopwatch.Elapsed.TotalMicroseconds > 1 ? $"{stopwatch.Elapsed.TotalMicroseconds:F1} μs" : $"{stopwatch.Elapsed.TotalNanoseconds:F1} ns"; } }