Xamarin Forms Android 应用程序上的 RestSharp 请求停顿
RestSharp Request Stalls on Xamarin Forms Android App
我正在构建的应用程序需要在 GKE 中调用一些 Api、运行 来检索一些值。在这种情况下,它是设置的默认值。
GKE 中的 Api 目前处于崩溃循环中,因为所需的数据库尚未设置。在 Api 关闭的情况下,就像现在一样,应用程序应该简单地在请求上超时,它从我的代码中应该处理得很好。
问题是,它永远不会超时,即使在 Postman 中完成的完全相同的请求在约 10 秒后超时也是如此。
我的 class 的构造函数执行对 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包,但是距离上次更新已经有一段时间了,我不太愿意使用它。
如有更多详细信息,请随意询问,如果还有什么我可以为您提供的。
在我的工作中,为了解决这个问题,我创建了一些代码来使客户端的请求超时。在创建这段代码的过程中,它突然开始工作,请求超时。
我不确定我做了什么更改,修复了它,所以我将展示我更新的 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 项目属性下打开或关闭“使用快速部署”似乎确实可以在未知的时间内“修复”thids 问题。
我正在构建的应用程序需要在 GKE 中调用一些 Api、运行 来检索一些值。在这种情况下,它是设置的默认值。
GKE 中的 Api 目前处于崩溃循环中,因为所需的数据库尚未设置。在 Api 关闭的情况下,就像现在一样,应用程序应该简单地在请求上超时,它从我的代码中应该处理得很好。
问题是,它永远不会超时,即使在 Postman 中完成的完全相同的请求在约 10 秒后超时也是如此。 我的 class 的构造函数执行对 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包,但是距离上次更新已经有一段时间了,我不太愿意使用它。
如有更多详细信息,请随意询问,如果还有什么我可以为您提供的。
在我的工作中,为了解决这个问题,我创建了一些代码来使客户端的请求超时。在创建这段代码的过程中,它突然开始工作,请求超时。
我不确定我做了什么更改,修复了它,所以我将展示我更新的 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 项目属性下打开或关闭“使用快速部署”似乎确实可以在未知的时间内“修复”thids 问题。