如何改进HTTP GET请求的泛型Get函数,使其支持数组等复杂类型参数
解决泛型GET请求适配复杂类型参数的问题
嘿,这个问题我之前也碰到过!你的Get函数之所以在处理带数组的类时失效,核心问题出在JsonConvert.DeserializeObject<Dictionary<string, string>>(data)这一步——当对象里有数组、嵌套对象这类非字符串/基础类型的属性时,Newtonsoft.Json没办法把它们转换成string类型的字典值,直接就抛出异常了。
下面给你两种可靠的改进方案,都能完美适配数组、嵌套对象等复杂类型:
方案一:用JObject解析对象生成查询参数(推荐)
这个方案基于你已经在使用的Newtonsoft.Json,不需要额外引入依赖,而且能灵活处理不同类型的参数:
public async Task<string> Get<T>(T item, string path, string authorisationToken = "") { // 使用using声明自动释放资源,避免手动Dispose的潜在问题 using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); if (!string.IsNullOrWhiteSpace(authorisationToken)) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorisationToken); } // 用JObject直接解析对象,支持所有JSON可序列化类型 var jObject = JObject.FromObject(item); var queryParams = new Dictionary<string, string>(); foreach (var property in jObject.Properties()) { switch (property.Value.Type) { case JTokenType.Array: // 数组处理:两种方式二选一,根据后端API的要求调整 // 方式1:逗号分隔字符串(比如 ?searchKeys=apple,banana) var arrayValues = string.Join(",", property.Value.Select(v => v.ToString())); queryParams.Add(property.Name, arrayValues); // 方式2:重复参数名(更符合RESTful常见规范,比如 ?searchKeys=apple&searchKeys=banana) // foreach (var element in property.Value) // { // queryParams.Add(property.Name, element.ToString()); // } break; case JTokenType.Object: // 嵌套对象处理:序列化为紧凑JSON字符串传递 queryParams.Add(property.Name, property.Value.ToString(Formatting.None)); break; default: // 基础类型直接转换为字符串 queryParams.Add(property.Name, property.Value.ToString()); break; } } // 拼接查询参数到路径 var fullPath = QueryHelpers.AddQueryString(path, queryParams); using var response = await httpClient.GetAsync(fullPath); // 可选:如果需要处理非2xx状态码,可以注释掉下面这行,自行处理错误 response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); return json; }
关键改进点:
- 用
JObject.FromObject替代“序列化再反序列化为字典”的冗余步骤,直接从对象提取属性,支持所有JSON兼容类型 - 针对数组、嵌套对象做了专门处理,你可以根据后端API的参数格式要求选择对应的处理方式
- 使用
using声明管理HttpClient和HttpResponseMessage,自动释放资源,避免内存泄漏
方案二:使用System.Text.Json(.NET Core 3.0+推荐)
如果你已经迁移到System.Text.Json(替代Newtonsoft.Json),可以用JsonDocument来实现类似的逻辑:
public async Task<string> Get<T>(T item, string path, string authorisationToken = "") { using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); if (!string.IsNullOrWhiteSpace(authorisationToken)) { httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorisationToken); } var jsonString = JsonSerializer.Serialize(item); using var jsonDoc = JsonDocument.Parse(jsonString); var queryParams = new Dictionary<string, string>(); void TraverseJsonElement(JsonElement element, string parentKey = "") { switch (element.ValueKind) { case JsonValueKind.Array: foreach (var arrayElement in element.EnumerateArray()) { // 这里同样可以选择逗号分隔或重复参数名 queryParams.Add(parentKey, arrayElement.ToString()); } break; case JsonValueKind.Object: foreach (var property in element.EnumerateObject()) { var key = string.IsNullOrEmpty(parentKey) ? property.Name : $"{parentKey}.{property.Name}"; TraverseJsonElement(property.Value, key); } break; default: queryParams.Add(parentKey, element.ToString()); break; } } TraverseJsonElement(jsonDoc.RootElement); var fullPath = QueryHelpers.AddQueryString(path, queryParams); using var response = await httpClient.GetAsync(fullPath); response.EnsureSuccessStatusCode(); var json = await response.Content.ReadAsStringAsync(); return json; }
测试验证
用你的MySimpleClassWithAnArray测试:
var testItem = new MySimpleClassWithAnArray { page = 2, searchKeys = new[] { "apple", "banana" } }; var result = await Get(testItem, "/api/products");
如果选择数组的“重复参数名”方式,生成的URL会是:/api/products?page=2&searchKeys=apple&searchKeys=banana,完全符合多数后端API的数组参数接收规范。
内容的提问来源于stack exchange,提问作者KDev
相关产品推荐
相关产品推荐

