【问题标题】:RestSharp Request Stalls on Xamarin Forms Android AppXamarin Forms Android 应用程序上的 RestSharp 请求停止
【发布时间】:2020-12-02 21:54:56
【问题描述】:

我正在构建的应用程序需要调用一些在 GKE 中运行的 Api 来检索一些值。在这种情况下,它是设置的默认值。

由于所需的数据库尚未设置,GKE 中的 Api 当前处于崩溃循环中。在 Api 出现故障的情况下,就像现在一样,应用程序应该只是在请求上超时,它从我的代码中应该可以处理得很好。

问题是,它永远不会超时,即使在 Postman 中完成的完全相同的请求在大约 10 秒后超时。 我的类的构造函数,对 Api 的调用如下所示。忽略 IHandler,如下所示,它适用于我的 Entity Framework Core。问题出在方法“ApiGet”内部。 'ApiGet' 方法是从我的 Get 方法中调用的,我不打算展示它,因为那是一种方法的怪物(112 行...)

public abstract class ControllerProxy
    {
        protected RestClient Client { get; }
        protected IHandler Handler { get; }

        protected ControllerProxy(IHandler handler, string baseAddress, string controller)
        {
            Client = new RestClient(new Uri($"{baseAddress}api/{controller}/"));
            Handler = handler;
        }
    }
public class SettingsControllerProxy : ControllerProxy, ISettingsController<SettingProxy>
    {
        public SettingsControllerProxy(IHandler handler, string baseAddress, string controller) : base(handler, baseAddress, controller)
        {
            Client.ThrowOnAnyError = true;
            Client.Timeout = 1000;
        }

        private async Task<IEnumerable<SettingProxy>> ApiGet(SettingProxy entity)
        {
            // Creates request for the settings matching the ValueType and key
            var request = new RestRequest() {Timeout = 1000, ReadWriteTimeout = 1000, Method = Method.GET }.AddQueryParametersFromObject(entity,
                new List<string>() {nameof(entity.ValueType), nameof(entity.Key)});

            try
            {
                var response = await Client.ExecuteTask(request, TimeSpan.FromSeconds(10));

                // Returns the Deserialized list of Settings if it is successful, otherwise gives an empty list. Check for empty list on the return side.
                return response.IsSuccessful ? JsonConvert.DeserializeObject<List<SettingProxy>>(response.Content) : new List<SettingProxy>();
            }
            catch (Exception e)
            {
                Logging.Log(LogType.Warning, $"Caught a {e.GetType().Name} in {nameof(ApiGet)}; " +
                                             $"Exception => {e}; returning an empty list to caller...");
                return new List<SettingProxy>();
            }
        }
        ...
    }

下面可以看到方法“AddQueryParametersFromObject”以供参考,然后是“ExecuteTask”。当我在 ExecuteTask 中调用 ExecuteAsync 时,它会停止并且永远不会继续。

public static IRestRequest AddQueryParametersFromObject<T>(this IRestRequest request, T input,
            IEnumerable<string> propertyNames) where T : class
        {
            var properties = typeof(T).GetProperties().Where(x => propertyNames.Any(z => x.Name == z)).ToList();

            return properties.Aggregate(request, (current, info) => current.AddQueryParameter(info.Name, info.GetValue(input).ToString()));
        }
public static async Task<IRestResponse> ExecuteTask(this IRestClient client, IRestRequest request, TimeSpan taskTimeout = default)
        {
            var TaskCompletionSource = new TaskCompletionSource<IRestResponse>();
            var source = new CancellationTokenSource();
            
            if (taskTimeout != default)
                source.CancelAfter(taskTimeout);

            try
            {
                var response = await client.ExecuteAsync(request, source.Token);

                if (response.ResponseStatus == ResponseStatus.Error)
                    TaskCompletionSource.SetException(response.ErrorException);
                else
                    TaskCompletionSource.SetResult(response);
            }
            catch (Exception e)
            {
                TaskCompletionSource.SetException(e);
            }

            return await TaskCompletionSource.Task;
        }

RestSharp 是否根本不适用于 Xamarin? 我想继续使用 RestSharp,因为用它创建请求似乎要容易得多,超过 HttpClient。

我查看了“FubarCoder.RestSharp.Portable”Nuget 包,但由于它自上次更新以来已经有一段时间了,我不愿意使用它。

如果还有什么我可以提供给你的,请随意询问更多细节。

【问题讨论】:

标签: c# xamarin.forms xamarin.android asp.net-core-webapi restsharp


【解决方案1】:

在我的工作中,试图解决这个问题,我创建了一些代码来超时客户端的请求。在创建此代码期间,它突然开始工作,请求超时。

我不确定我做了什么改变,修复了它,所以我将展示我更新的 ApiGet 和 ExecuteTask 方法。

private async Task<IEnumerable<SettingProxy>> ApiGet(SettingProxy entity)
        {
            // Creates request for the settings matching the ValueType and key
            var request = new RestRequest() { Timeout = 2000, ReadWriteTimeout = 2000, Method = Method.GET }.AddQueryParametersFromObject(entity,
                new List<string>() { nameof(entity.ValueType), nameof(entity.Key) });

            try
            {
                var response = await Client.Client.ExecuteTask(request, TimeSpan.FromSeconds(6));

                // Returns the Deserialized list of Settings if it is successful, otherwise gives an empty list. Check for empty list on the return side.
                return response.IsSuccessful ? JsonConvert.DeserializeObject<List<SettingProxy>>(response.Content) : new List<SettingProxy>();
            }
            catch (Exception e)
            {
                Logging.Log(LogType.Warning, $"Caught a {e.GetType().Name} in {nameof(ApiGet)}, while attempting to get settings for key: {entity.Key}; " +
                                             $"Exception => {e}; returning an empty list to caller...");
                return new List<SettingProxy>();
            }
        }
public static async Task<IRestResponse> ExecuteTask(this IRestClient client, IRestRequest request, TimeSpan timeout = default)
        {
            var TaskCompletionSource = new TaskCompletionSource<IRestResponse>();
            var source = new CancellationTokenSource();
            
            if (timeout != default)
                request.OnBeforeRequest += http =>
                {
                    http.Timeout = (int) timeout.TotalMilliseconds;
                };

            Task<IRestResponse> run = null;

            try
            {
                run = client.ExecuteAsync(request, source.Token);

                if (timeout != default)
                {
                    var tasks = new[] { run, Task.Delay(timeout, source.Token) };
                    var finished = Task.WaitAny(tasks);
                    
                    if (!run.IsCompleted && finished == 1)
                    {
                        source.Cancel();
                        throw new TimeoutException($"Request timed out by Client Code, to prevent stall.");
                    }
                }

                var response = await run;

                if (response.ResponseStatus != ResponseStatus.Completed)
                    TaskCompletionSource.SetException(response.ErrorException);
                else
                    TaskCompletionSource.SetResult(response);
            }
            catch (Exception e)
            {
                TaskCompletionSource.SetException(e);
            }

            run?.Dispose();

            return await TaskCompletionSource.Task;
        }

运行我的代码,目前主要是客户端代码使请求超时,但在一段时间内,它是一个 WebException,表示超时,被抛出。

我有一种预感,这个问题是由于我的 Xamarin Forms 没有部署最新的更改引起的,而我的项目一直受到这些更改的困扰。在 Android 项目属性下打开或关闭“使用快速部署”似乎确实可以“修复”一些未知时间的问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 1970-01-01
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    相关资源
    最近更新 更多