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

.NET泛型API仓储中ReadFromJsonAsync<List<T>>反序列化带包装的API响应时抛JsonException的解决方法

.NET泛型API仓储中ReadFromJsonAsync<List>反序列化带包装的API响应时抛JsonException的解决方法

这个问题我太熟悉了——你踩的坑是典型的JSON结构不匹配导致的反序列化失败。API返回的是一个包装后的对象(根节点包含data、message、isSuccess三个属性),但你的代码直接尝试把整个响应反序列化成List<T>类型,两者的结构完全对不上,自然会抛出JsonException。

下面给你一套通用的解决方案,完美适配你的分层项目结构:


第一步:定义通用的API响应包装类

因为你的所有API返回结构都是统一的(有数据集合、消息、成功标识),我们先创建一个泛型类来对应这个结构,把它放在Core层或者Model层,方便全项目复用:

using System.Text.Json.Serialization;

// 通用API响应模型,匹配返回的JSON结构
public class ApiResponse<T>
{
    // 用JsonPropertyName显式映射JSON的小驼峰字段
    [JsonPropertyName("data")]
    public T Data { get; set; }

    [JsonPropertyName("message")]
    public string Message { get; set; }

    [JsonPropertyName("isSuccess")]
    public bool IsSuccess { get; set; }
}

这里用JsonPropertyName特性是为了严格匹配JSON的小驼峰字段,避免大小写不匹配的问题;如果你不想写特性,也可以在反序列化时开启PropertyNameCaseInsensitive = true,不过显式映射更严谨。


第二步:修改泛型仓储的反序列化逻辑

接下来要修改ApiRepositoryBase.cs里的GetListAsync方法,把反序列化的目标从List<T>改成ApiResponse<List<T>>,先解析整个API响应,再提取里面的data集合:

public async Task<IDataResult<List<T>>> GetListAsync(string endpoint, Expression<Func<T, bool>>? filter = null)
{
    try
    {
        using var httpClient = new HttpClient();
        httpClient.BaseAddress = new Uri(_baseUrl.Trim());

        httpClient.DefaultRequestHeaders.Authorization =
            new AuthenticationHeaderValue("Bearer", _token);

        var response = await httpClient.GetAsync($"api/{endpoint}");

        if (!response.IsSuccessStatusCode)
            return new ErrorDataResult<List<T>>(response.ReasonPhrase ?? "Api error");

        // 配置反序列化选项(如果用了JsonPropertyName,这一步可以省略)
        var jsonOptions = new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true
        };

        // 先反序列化成通用API响应模型
        var apiResponse = await response.Content.ReadFromJsonAsync<ApiResponse<List<T>>>(jsonOptions);

        if (apiResponse == null)
            return new ErrorDataResult<List<T>>("Failed to parse API response");

        // 利用API返回的IsSuccess判断请求是否真正成功
        if (!apiResponse.IsSuccess)
            return new ErrorDataResult<List<T>>(apiResponse.Message ?? "API request failed");

        // 提取核心数据集合
        var data = apiResponse.Data;
        if (data == null)
            return new ErrorDataResult<List<T>>("No data returned from API");

        // 原有过滤逻辑保持不变
        if (filter != null)
        {
            var compiledFilter = filter.Compile();
            data = data.Where(compiledFilter).ToList();
        }

        return new SuccessDataResult<List<T>>(data);
    }
    catch (Exception ex)
    {
        return new ErrorDataResult<List<T>>(ex.Message);
    }
}

为什么这样改能解决问题?

  1. 现在我们用ApiResponse<List<T>>来匹配API返回的完整JSON结构,反序列化时就能正确解析整个响应内容,不会再因为根节点类型不匹配报错。
  2. 我们还能利用API返回的IsSuccess和Message字段做更精准的错误处理,而不是只依赖HTTP状态码——比如有时候HTTP返回200,但业务逻辑上请求是失败的(比如参数错误),这时候就能通过IsSuccess判断出来。
  3. 这个方案是完全通用的,不管你后续加什么新的实体(比如Kitap、Yazar等),都能复用这个ApiResponse<T>和修改后的仓储方法,不用重复写代码。

备注:内容来源于stack exchange,提问作者NEX

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 18:38:00