Xamarin.Forms服务如何在返回List<T>的同时向ViewModel传递状态码
服务层返回状态码与业务数据的实现方案
原有单返回值的方法仅能承载一类对象,无法同时传递照片列表与HTTP状态码这类请求上下文元数据,需要通过结构化的返回模型承载不同类型的结果。
通用API响应包装(推荐最佳实践)
该方案是分层架构下的通用实现,兼顾可复用性、扩展性与维护性。
- 先定义全项目可复用的泛型响应包装类,用于统一承载状态码、业务数据、错误信息三类核心内容:
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; }
- 重构原有
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; } }
- 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
相关产品推荐
相关产品推荐

