You need to enable JavaScript to run this app. Xamarin.Forms服务如何在返回List<T>的同时向ViewModel传递状态码
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin.Forms服务如何在返回List<T>的同时向ViewModel传递状态码

服务层返回状态码与业务数据的实现方案

原有单返回值的方法仅能承载一类对象,无法同时传递照片列表与HTTP状态码这类请求上下文元数据,需要通过结构化的返回模型承载不同类型的结果。

通用API响应包装(推荐最佳实践)

该方案是分层架构下的通用实现,兼顾可复用性、扩展性与维护性。

  1. 先定义全项目可复用的泛型响应包装类,用于统一承载状态码、业务数据、错误信息三类核心内容:
public class ApiResponse<T>
{
    //  HTTP响应状态码,本地错误可通过自定义负值做区分
    public int StatusCode { get; set; }
    //  接口返回的实际业务数据
    public T Data { get; set; }
    //  错误提示文案,ViewModel可直接复用做Toast/弹窗提示
    public string ErrorMessage { get; set; }
    //  辅助属性:快速判断请求是否成功
    public bool IsSuccess => StatusCode is >= 200 and < 300;
}
  1. 重构原有GetAllPosts方法的返回值逻辑,将所有分支的返回结果替换为结构化的包装对象:
public async Task<ApiResponse<IList<Photo>>> GetAllPosts()
{
    var response = new ApiResponse<IList<Photo>>();
    try
    {
        if (!CrossConnectivity.Current.IsConnected)
        {
            // 本地无网络场景,用自定义状态码区分
            response.StatusCode = -1;
            response.ErrorMessage = "当前无可用网络,请检查网络设置";
            return response;
        }

        instagramCloneClient.DefaultRequestHeaders.Clear();
        instagramCloneClient.DefaultRequestHeaders.Add("Accept", "application/json");
        var httpResponse = await instagramCloneClient.GetAsync("/photos");
        //  直接捕获HTTP状态码赋值
        response.StatusCode = (int)httpResponse.StatusCode;

        if (httpResponse.IsSuccessStatusCode)
        {
            string jsonMessage;
            using (Stream responseStream = await httpResponse.Content.ReadAsStreamAsync())
            {
                jsonMessage = new StreamReader(responseStream).ReadToEnd();
            }
            response.Data = JsonConvert.DeserializeObject<IList<Photo>>(jsonMessage);
            return response;
        }

        // 非成功HTTP状态码场景,可按需补充读取错误响应内容的逻辑
        response.ErrorMessage = "获取照片列表失败,请稍后重试";
        return response;
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
        // 本地请求异常场景,用自定义状态码区分
        response.StatusCode = -2;
        response.ErrorMessage = $"请求发生异常:{ex.Message}";
        return response;
    }
}
  1. ViewModel层直接使用统一包装类的属性做业务判断,不需要额外解析逻辑:
// ViewModel内调用服务的逻辑
var apiResult = await _photoService.GetAllPosts();

if (apiResult.IsSuccess)
{
    //  直接绑定成功返回的列表数据
    PhotoList = new ObservableCollection<Photo>(apiResult.Data);
}
else
{
    //  可根据StatusCode做差异化分支处理
    switch (apiResult.StatusCode)
    {
        case -1:
            ToastService.Show(apiResult.ErrorMessage);
            break;
        case 401:
            // 未授权状态直接跳转登录页
            NavigationService.NavigateToLogin();
            break;
        case 500:
            ToastService.Show("服务器暂时不可用,请稍后再试");
            break;
        default:
            ToastService.Show(apiResult.ErrorMessage);
            break;
    }
}

方案优势

  • 分层边界清晰:服务层仅负责采集网络响应的原始信息、封装成统一结构,不耦合UI提示逻辑,完全符合MVVM的职责分离原则
  • 全项目可复用:泛型设计可适配所有服务接口的返回需求,不需要为每个接口单独设计返回结构
  • 扩展性强:后续需要新增请求耗时、错误详情、分页元数据等附加信息时,直接在包装类中新增属性即可,不需要逐一对每个服务方法的签名做修改
  • 降低空引用风险:原方法遇到任何错误都返回null,ViewModel很容易因为遗漏判空触发崩溃,统一包装类返回固定结构后,只需要对业务数据字段做判空判断即可,逻辑更可控

轻量替代方案:值元组

如果项目规模很小,不想额外编写包装类,可以使用C# 7+自带的值元组快速组合返回状态码和业务数据:

public async Task<(int StatusCode, IList<Photo> Photos)> GetAllPosts()
{
    // 内部请求逻辑与原有实现一致
    // 无网场景:return (-1, null);
    // 请求成功:return (200, photosList);
    // 非成功状态:return ((int)httpResponse.StatusCode, null);
}

// ViewModel调用示例
var (status, photoList) = await GetAllPosts();

该方案的优点是零额外代码、开发速度快,但缺点是没有统一的返回结构规范,后续要新增其他返回字段时,需要修改所有相关服务方法的返回签名,维护成本较高,不适合中大型项目。

内容的提问来源于stack exchange,提问作者Rajzer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.03 05:06:08