Xamarin使用HttpClient调用.Net Core Web API超时设置不生效求助
问题根因
你之前的超时方案未生效主要有3个核心原因:
- 即使启用了
CancellationTokenSource.CancelAfter,也没有将取消令牌传入PostAsync方法,超时触发时无法终止请求 - 调用
response.Content.ReadAsStringAsync().Result使用同步阻塞方式读取响应,极易造成线程死锁,导致超时逻辑无法正常触发 - 每次请求都新建
HttpClient实例,不仅会导致socket资源泄漏,也可能让部分平台下的Timeout配置不生效
修正方案
第一步:调整HttpClient为单例模式
在你的API调用类中定义静态HttpClient实例,全局复用:
// 类级别静态变量,不要每次请求新建 private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler{AllowAutoRedirect = true}) { Timeout = TimeSpan.FromSeconds(10) // 全局默认超时时间 };
第二步:改造Post方法,正确使用取消令牌+异步读取响应
修改后的完整Post方法如下:
public async Task<BaseResponseModel> Post(BasePostModel postModel) { var responseModel = new BaseResponseModel(); var isPosted = true; var url = GetURL(postModel.UrlKey); var settings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore, ContractResolver = new DefaultContractResolver(), ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // 可选:提前判断网络状态,无网直接返回错误(依赖Xamarin.Essentials) if (Connectivity.NetworkAccess != NetworkAccess.Internet) { responseModel.Error = "网络异常,请检查您的网络设置"; responseModel.Message = "网络异常,请检查您的网络设置"; return responseModel; } // 初始化取消令牌,10秒超时 using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); try { // 配置请求头 _httpClient.DefaultRequestHeaders.Clear(); _httpClient.DefaultRequestHeaders.Add("Accept", "application/json"); _httpClient.DefaultRequestHeaders.Add("X-Env", "MOBILE_API"); AttachToken(ref _httpClient, responseModel.Id); var json = postModel.ToString(); var jsonParam = new StringContent(json, Encoding.UTF8, "application/json"); // 统一处理Token过期逻辑 if (Preferences.ContainsKey("UserJwtExprieDate")) { var expiryDate = Preferences.Get("UserJwtExprieDate", null); if (DateTime.Now > DateTime.Parse(expiryDate)) { Preferences.Remove("UserJwtExprieDate"); Preferences.Remove("HomePageInformation"); int index = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.Count - 1; Page currPage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack[index]; if (currPage as SigninForFactorOne == null) { App.LogoutUser(); } responseModel.Error = "登录已过期,请重新登录"; responseModel.Message = "登录已过期,请重新登录"; return responseModel; } } // 传入取消令牌,超时自动终止请求 var response = await _httpClient.PostAsync(url, jsonParam, cts.Token); if (response.IsSuccessStatusCode) { // 改用await异步读取,避免线程死锁 string result = await response.Content.ReadAsStringAsync(); var resultModel = JsonConvert.DeserializeObject<BaseResponseModel>(result, settings); if (resultModel.ErrorType == APIErrorTypes.NULL) { if (resultModel.IsSucceed) { responseModel.Data = resultModel.Data; } else { responseModel.Error = resultModel.Error; } responseModel.Message = resultModel.Message; } else { responseModel.Error = "登录已过期,请重新登录"; Preferences.Remove("UserJwtExprieDate"); Preferences.Remove("HomePageInformation"); App.LogoutUser(); } } else { new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id); isPosted = false; } } // 单独捕获超时异常,自定义提示 catch (OperationCanceledException) { isPosted = false; responseModel.Error = "请求超时,请检查网络后重试"; responseModel.Message = "请求超时,请检查网络后重试"; } catch (Exception ex) { new AppException(ex, responseModel.Id, 500, "anonymous.user", "Unable to post data to API!"); isPosted = false; } finally { if (!isPosted && string.IsNullOrEmpty(responseModel.Error)) { responseModel.Error = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage); responseModel.Message = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage); } } return responseModel; }
额外注意点
- 调用该Post方法的上层代码不要用
.Result或者.Wait()阻塞,全程用await异步调用,避免UI线程死锁 - 上层业务代码拿到返回的responseModel后,判断Error字段不为空时,即可关闭加载动画、弹出对应的用户提示
内容的提问来源于stack exchange,提问作者mkartal
相关产品推荐
相关产品推荐

