using NCC.ClayObject.Extensions; using NCC.DataValidation; using NCC.FriendlyException; using NCC.JsonSerialization; using NCC.Templates.Extensions; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; namespace NCC.RemoteRequest { /// /// HttpClient 对象组装部件 /// public sealed partial class HttpRequestPart { /// /// 请求失败事件 /// public event EventHandler OnRequestFailded; /// /// MiniProfiler 分类名 /// private const string MiniProfilerCategory = "httpclient"; /// /// 发送 GET 请求返回 T 对象 /// /// /// /// public Task GetAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Get).SendAsAsync(cancellationToken); } /// /// 发送 GET 请求返回 Stream /// /// /// public Task GetAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Get).SendAsStreamAsync(cancellationToken); } /// /// 发送 GET 请求返回 String /// /// /// public Task GetAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Get).SendAsStringAsync(cancellationToken); } /// /// 发送 GET 请求 /// /// /// public Task GetAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Get).SendAsync(cancellationToken); } /// /// 发送 POST 请求返回 T 对象 /// /// /// /// public Task PostAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Post).SendAsAsync(cancellationToken); } /// /// 发送 POST 请求返回 Stream /// /// /// public Task PostAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Post).SendAsStreamAsync(cancellationToken); } /// /// 发送 POST 请求返回 String /// /// /// public Task PostAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Post).SendAsStringAsync(cancellationToken); } /// /// 发送 POST 请求 /// /// /// public Task PostAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Post).SendAsync(cancellationToken); } /// /// 发送 PUT 请求返回 T 对象 /// /// /// /// public Task PutAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Put).SendAsAsync(cancellationToken); } /// /// 发送 PUT 请求返回 Stream /// /// /// public Task PutAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Put).SendAsStreamAsync(cancellationToken); } /// /// 发送 PUT 请求返回 String /// /// /// public Task PutAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Put).SendAsStringAsync(cancellationToken); } /// /// 发送 PUT 请求 /// /// /// public Task PutAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Put).SendAsync(cancellationToken); } /// /// 发送 DELETE 请求返回 T 对象 /// /// /// /// public Task DeleteAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Delete).SendAsAsync(cancellationToken); } /// /// 发送 DELETE 请求返回 Stream /// /// /// public Task DeleteAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Delete).SendAsStreamAsync(cancellationToken); } /// /// 发送 DELETE 请求返回 String /// /// /// public Task DeleteAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Delete).SendAsStringAsync(cancellationToken); } /// /// 发送 DELETE 请求 /// /// /// public Task DeleteAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Delete).SendAsync(cancellationToken); } /// /// 发送 PATCH 请求返回 T 对象 /// /// /// /// public Task PatchAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Patch).SendAsAsync(cancellationToken); } /// /// 发送 PATCH 请求返回 Stream /// /// /// public Task PatchAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Patch).SendAsStreamAsync(cancellationToken); } /// /// 发送 Patch 请求返回 String /// /// /// public Task PatchAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Patch).SendAsStringAsync(cancellationToken); } /// /// 发送 PATCH 请求 /// /// /// public Task PatchAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Patch).SendAsync(cancellationToken); } /// /// 发送 HEAD 请求返回 T 对象 /// /// /// /// public Task HeadAsAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Head).SendAsAsync(cancellationToken); } /// /// 发送 HEAD 请求返回 Stream /// /// /// public Task HeadAsStreamAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Head).SendAsStreamAsync(cancellationToken); } /// /// 发送 Head 请求返回 String /// /// /// public Task HeadAsStringAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Head).SendAsStringAsync(cancellationToken); } /// /// 发送 HEAD 请求 /// /// /// public Task HeadAsync(CancellationToken cancellationToken = default) { return SetHttpMethod(HttpMethod.Head).SendAsync(cancellationToken); } /// /// 发送请求返回 T 对象 /// /// /// /// public async Task SendAsAsync(CancellationToken cancellationToken = default) { // 如果 T 是 HttpResponseMessage 类型,则返回 if (typeof(T) == typeof(HttpResponseMessage)) { var httpResponseMessage = await SendAsync(cancellationToken); return (T)(object)httpResponseMessage; } if (typeof(T) == typeof(string)) { var str = await SendAsStringAsync(cancellationToken); return (T)(object)str; } // 读取流内容 var stream = await SendAsStreamAsync(cancellationToken); if (stream == null) return default; // 如果 T 是 Stream 类型,则返回 if (typeof(T) == typeof(Stream)) return (T)(object)stream; using var streamReader = new StreamReader(stream); var text = await streamReader.ReadToEndAsync(); // 释放流 await stream.DisposeAsync(); // 如果字符串为空,则返回默认值 if (string.IsNullOrWhiteSpace(text)) return default; // 解析 Json 序列化提供器 var jsonSerializer = App.GetService(JsonSerializerProvider, RequestScoped ?? App.RootServices) as IJsonSerializerProvider; // 反序列化流 var result = jsonSerializer.Deserialize(text, JsonSerializerOptions); return result; } /// /// 发送请求返回 Stream /// /// /// public async Task SendAsStreamAsync(CancellationToken cancellationToken = default) { var response = await SendAsync(cancellationToken); if (response == null) return default; // 读取响应流 var stream = await response.Content.ReadAsStreamAsync(cancellationToken); return stream; } /// /// 发送请求返回 String /// /// /// public async Task SendAsStringAsync(CancellationToken cancellationToken = default) { var response = await SendAsync(cancellationToken); if (response == null) return default; // 读取响应报文 var content = await response.Content.ReadAsStringAsync(cancellationToken); return content; } /// /// 发送请求 /// /// /// public async Task SendAsync(CancellationToken cancellationToken = default) { // 检查是否配置了请求方法 if (Method == null) throw new NullReferenceException(nameof(Method)); // 检查请求地址,只有 Client 不配置才检查空 if (string.IsNullOrWhiteSpace(ClientName) && string.IsNullOrWhiteSpace(RequestUrl)) throw new NullReferenceException(RequestUrl); // 处理模板问题 RequestUrl = RequestUrl.Render(Templates, true); // 构建请求对象 var request = new HttpRequestMessage(Method, RequestUrl); request.AppendQueries(Queries); // 设置请求报文头 if (Headers != null) { foreach (var header in Headers) { if (header.Value != null) request.Headers.Add(header.Key, header.Value.ToString()); } } // 验证模型参数(只作用于 body 类型) if (ValidationState.Enabled) { // 判断是否启用 Null 验证且 body 值为 null if (ValidationState.IncludeNull && Body == null) throw new InvalidOperationException($"{nameof(Body)} can not be null."); // 验证模型 Body?.Validate(); } // 设置 HttpContent SetHttpContent(request); // 配置请求拦截 RequestInterceptors.ForEach(u => { u?.Invoke(request); }); // 创建客户端请求工厂 var clientFactory = App.GetService(RequestScoped ?? App.RootServices); if (clientFactory == null) throw new InvalidOperationException("please add `services.AddRemoteRequest()` in Startup.cs."); // 创建 HttpClient 对象 using var httpClient = string.IsNullOrWhiteSpace(ClientName) ? clientFactory.CreateClient() : clientFactory.CreateClient(ClientName); // 只有大于 0 才设置超时时间 if (Timeout > 0) { // 设置请求超时时间 httpClient.Timeout = TimeSpan.FromSeconds(Timeout); } // 判断命名客户端是否配置了 BaseAddress,且必须以 / 结尾 var httpClientOriginalString = httpClient.BaseAddress?.OriginalString; if (!string.IsNullOrWhiteSpace(httpClientOriginalString) && !httpClientOriginalString.EndsWith("/")) throw new InvalidOperationException($"The `{ClientName}` of HttpClient BaseAddress must be end with '/'."); // 添加默认 User-Agent if (!httpClient.DefaultRequestHeaders.Contains("User-Agent")) { httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Safari/537.36 Edg/91.0.864.48"); } // 配置 HttpClient 拦截 HttpClientInterceptors.ForEach(u => { u?.Invoke(httpClient); }); // 打印发送请求 App.PrintToMiniProfiler(MiniProfilerCategory, "Sending", $"[{Method}] {httpClientOriginalString}{request.RequestUri?.OriginalString}"); // 捕获异常 Exception exception = default; HttpResponseMessage response = default; try { if (RetryPolicy == null) response = await httpClient.SendAsync(request, cancellationToken); else { // 失败重试 await Retry.Invoke(async () => { // 发送请求 response = await httpClient.SendAsync(request, cancellationToken); }, RetryPolicy.Value.NumRetries, RetryPolicy.Value.RetryTimeout); } } catch (Exception ex) { // 触发自定义事件 OnRequestFailded(this, new HttpRequestFaildedEventArgs(request, response, ex)); exception = ex; } // 请求成功 if (response?.IsSuccessStatusCode == true && exception == default) { // 打印成功请求 App.PrintToMiniProfiler(MiniProfilerCategory, "Succeeded", $"[StatusCode: {response.StatusCode}] Succeeded"); // 调用成功拦截器 ResponseInterceptors.ForEach(u => { u?.Invoke(response); }); } // 请求异常 else { // 读取错误消息 var errors = exception == null ? await response.Content.ReadAsStringAsync(cancellationToken) : exception.Message; // 打印失败请求 App.PrintToMiniProfiler(MiniProfilerCategory, "Failed", $"[StatusCode: {response?.StatusCode}] {errors}", true); // 抛出异常 if (ExceptionInterceptors == null || ExceptionInterceptors.Count == 0) throw new HttpRequestException(errors); // 调用异常拦截器 else ExceptionInterceptors.ForEach(u => { u?.Invoke(response, errors); }); } return response; } /// /// 设置 HttpContent /// /// private void SetHttpContent(HttpRequestMessage request) { // GET/HEAD 请求不支持设置 Body 请求 if (Method == HttpMethod.Get || Method == HttpMethod.Head) return; HttpContent httpContent = null; // 处理各种 Body 类型 switch (ContentType) { case "multipart/form-data": var multipartFormDataContent = new MultipartFormDataContent(); // 添加 Bytes 类型 foreach (var (Name, Bytes, FileName) in BodyBytes) { if (string.IsNullOrWhiteSpace(FileName)) multipartFormDataContent.Add(new ByteArrayContent(Bytes), Name); else multipartFormDataContent.Add(new ByteArrayContent(Bytes), Name, FileName); } // 处理其他类型 var dic = ConvertBodyToDictionary(); if (dic != null && dic.Count > 0) { foreach (var (key, value) in dic) { multipartFormDataContent.Add(new StringContent(value, ContentEncoding), string.Format("\"{0}\"", key)); } } // 设置内容类型 multipartFormDataContent.Headers.ContentType = new MediaTypeHeaderValue(ContentType); httpContent = multipartFormDataContent; break; case "application/octet-stream": if (BodyBytes.Count > 0 && BodyBytes[0].Bytes.Length > 0) { httpContent = new ByteArrayContent(BodyBytes[0].Bytes); // 设置内容类型 httpContent.Headers.ContentType = new MediaTypeHeaderValue(ContentType); } break; case "application/json": case "text/json": case "application/*+json": if (Body != null) { httpContent = new StringContent(SerializerObject(Body), ContentEncoding); // 设置内容类型 httpContent.Headers.ContentType = new MediaTypeHeaderValue(ContentType); } break; case "application/x-www-form-urlencoded": // 解析字典 var keyValues = ConvertBodyToDictionary(); if (keyValues == null || keyValues.Count == 0) return; // 设置内容类型 httpContent = new FormUrlEncodedContent(keyValues); break; case "application/xml": case "text/xml": if (Body != null) httpContent = new StringContent(Body.ToString(), ContentEncoding, ContentType); break; default: // 其他类型可通过 `HttpRequestMessage` 拦截器设置 break; } // 设置 HttpContent if (httpContent != null) request.Content = httpContent; } /// /// 转换 Body 为 字典类型 /// /// private IDictionary ConvertBodyToDictionary() { IDictionary keyValues = null; if (Body == null) return default; // 处理各种情况 if (Body is IDictionary dic) keyValues = dic; else if (Body is IDictionary dicObj) keyValues = dicObj.ToDictionary(u => u.Key, u => SerializerObject(u.Value)); else keyValues = Body.ToDictionary().ToDictionary(u => u.Key, u => SerializerObject(u.Value)); return keyValues; } /// /// 序列化对象 /// /// /// private string SerializerObject(object body) { if (body == null) return default; if (body is string) return body as string; if (body.GetType().IsValueType) return body.ToString(); // 解析序列化工具 var jsonSerializer = App.GetService(JsonSerializerProvider, RequestScoped ?? App.RootServices) as IJsonSerializerProvider; return jsonSerializer.Serialize(body, JsonSerializerOptions); } } }