You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改进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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 03:27:49