Xamarin Http 请求超时问题

Xamarin Http Request Timeout Issue

我有一个基于 Xamarin 的移动应用程序和一个基于 .Net Core 的 Web API。移动应用程序通过 HttpClient 使用 Web API 的方法。下面的代码是我调用任何 Web API 方法的基本方法,重点是我想设置超时但无论我实现了什么都无法设置准确的超时值。尝试了 Timespan.FromSeconds() 或 TimeSpan.FromMilliseconds() 等。当客户端向 Web API 发出请求时,显示加载程序以锁定 UI 并在 API 之后删除回复。一些客户给我的反馈是加载程序永远显示并且请求永远不会结束。也许,服务器在这个特定时间无法访问,或者客户端的互联网连接中断等。我只想设置超时并中断请求并向客户端显示警报消息。当然,我用谷歌搜索并尝试了很多,但没有结果。如果有人能帮助我,将不胜感激。

    public async Task<BaseResponseModel> Post(BasePostModel postModel)
        {
            var responseModel = new BaseResponseModel();
            var json = postModel.ToString();
            var jsonParam = new StringContent(json, Encoding.UTF8, "application/json");
            var isPosted = true;
            var clientHandler = new HttpClientHandler()
            {
                AllowAutoRedirect = true,
                
            };

            var url = GetURL(postModel.UrlKey);

            var settings = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                MissingMemberHandling = MissingMemberHandling.Ignore,
                ContractResolver = new DefaultContractResolver(),
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            };

            var client = new HttpClient(clientHandler);
            //client.Timeout = TimeSpan.FromSeconds(10);
            //var cancellationTokenSource = new CancellationTokenSource();
            //cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(10));

            client.DefaultRequestHeaders.Add("Accept", "application/json");
            client.DefaultRequestHeaders.Add("X-Env", "MOBILE_API");
            AttachToken(ref client, responseModel.Id);

            try
            {
                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)
                        {}
                        else
                        {
                            App.LogoutUser();
                        }    
                    }
                    else
                    {
                        var response = await client.PostAsync(url, jsonParam);
                        if (response.IsSuccessStatusCode)
                        {
                            string result = response.Content.ReadAsStringAsync().Result;
                            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 = "Token Expried Date.";
                                Preferences.Remove("UserJwtExprieDate");
                                Preferences.Remove("HomePageInformation");
                                App.LogoutUser();
                            }
                        }
                        else
                        {
                            new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
                            isPosted = false;
                        }
                    }
                }
                else
                {
                    var response = await client.PostAsync(url, jsonParam);
                    if (response.IsSuccessStatusCode)
                    {
                        string result = response.Content.ReadAsStringAsync().Result;
                        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 = "Token Expried Date.";
                            Preferences.Remove("UserJwtExprieDate");
                            Preferences.Remove("HomePageInformation");
                            App.LogoutUser();
                        }
                    }
                    else
                    {
                        new AppException(new Exception("HTTP Client response is not succeed!"), responseModel.Id);
                        isPosted = false;
                    }
                }
               
            }
            catch (Exception ex)
            {
                new AppException(ex, responseModel.Id, 500, "anonymous.user", "Unable to post data to API!");
                isPosted = false;
            }
            finally
            {
                if (!isPosted)
                {
                    responseModel.Error = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
                    responseModel.Message = AppConfiguration.GetSystemMessage(contactYourSystemAdministratorMessage);
                }
            }

            return responseModel;
        }

我已经使用下面的解决方案手动设置了一个工作正常的超时。

internal class TimeOutHandler : DelegatingHandler
{
    private readonly TimeSpan TimeOut;

    public TimeOutHandler(TimeSpan timeOut) => TimeOut = timeOut;

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage req, CancellationToken ct)
    {
        using (var ctTimeOut = CancellationTokenSource.CreateLinkedTokenSource(ct))
        {
            ctTimeOut.CancelAfter(TimeOut);

            try
            {
                return await base.SendAsync(req, ctTimeOut.Token);
            }
            catch (OperationCanceledException) when (!ct.IsCancellationRequested)
            {
                throw new TimeoutException();
            }
        }
    }
}

如何使用

var interval = TimeSpan.FromSeconds(10);
var handler = new TimeOutHandler(interval)
{
    InnerHandler = new HttpClientHandler()
};

var client = new HttpClient(handler);

有关更多信息,请查看:https://thomaslevesque.com/2018/02/25/better-timeout-handling-with-httpclient/