【发布时间】:2016-05-21 23:53:46
【问题描述】:
晚上好,
我正在尝试使用异步编程来优化一些代码,想知道以下代码是否写得好,是否有任何改进方法。
它的目的是从给定的 URL 下载一些 JSON 并将其反序列化为一个对象。
我有 3 个(现在是 4 个)问题(帖子末尾描述了 1 个问题):
- 我应该使用
TaskEx.Run运行吗Newtonsoft.Json.JsonConvert.DeserializeObject<T>? - 有什么好的方法(不检查对象属性)
知道
rootObject是否创建成功? - 我是否应该在某处检查是否要求取消?
- (新问题)我应该在发出新请求之前取消挂起请求吗?
废话不多说,代码如下:
internal static class WebUtilities
{
/// <summary>
/// Downloads the page of the given url
/// </summary>
/// <param name="url">url to download the page from</param>
/// <param name="cancellationToken">token to cancel the download</param>
/// <returns>the page content</returns>
internal static async Task<string> DownloadStringAsync(string url, CancellationToken cancellationToken)
{
try
{
// create Http Client and dispose of it even if exceptions are thrown (same as using finally statement)
using (var client = new HttpClient() {Timeout = TimeSpan.FromSeconds(5)})
{
// should I always do this?
client.CancelPendingRequests();
// do request and dispose of it when done
using (var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false))
{
// if response was successful (otherwise it will return null)
if (response.IsSuccessStatusCode)
{
// return its content
return await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
}
}
catch (Exception ex) when (ex is System.Net.Sockets.SocketException ||
ex is InvalidOperationException ||
ex is OperationCanceledException ||
ex is System.Net.Http.HttpRequestException)
{
WriteLine("DownloadStringAsync task has been cancelled.");
WriteLine(ex.Message);
return null;
}
// return null if response was unsuccessful
return null;
}
/// <summary>
/// Downloads Json from a given url and attempts its deserialization to a given reference type (class)
/// </summary>
/// <typeparam name="T">the class to deserialize to</typeparam>
/// <param name="url">url to download the json from</param>
/// <param name="cancellationToken">token to cancel the download</param>
/// <returns>the deserialized object</returns>
internal static async Task<T> DownloadJsonAndDeserialize<T>(string url, CancellationToken cancellationToken) where T : class, new()
{
// download json from the given url
var jsonResponse = await DownloadStringAsync(url, cancellationToken).ConfigureAwait(false);
// if the response is invalid, no need to go further
if (string.IsNullOrEmpty(jsonResponse))
// return a default constructor instance
return new T();
// try to deserialize
try
{
// Deserialize json data to the given .NET object
// Should I use TaskEx.Run here?
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(jsonResponse);
}
catch (Exception ex) when (ex is JsonException)
{
WriteLine("Something went wrong while deserializing json to the given reference type.");
WriteLine(ex.Message);
}
// return a default constructor instance
return new T();
}
}
要调用代码,可以执行以下操作:
internal static async Task CallAsync()
{
RootObject root = null;
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
var token = cts.Token;
root = await WebUtilities.DownloadJsonAndDeserialize<RootObject>(URL, token).ConfigureAwait(false);
}
// do something with rootObject
// any good way to know if rootObject was successfully created, before moving on?
}
你会改变什么?为什么?
谢谢!
编辑:
- @codran 建议 - 每个
await现在都在使用ConfigureAwait(false) -
CancellationTokenSource现已处理(使用语句) - @codran 建议 - 现在过滤了一些例外情况
-
DownloadStringAsync现在在try-catch中有 2 个using语句 块以提高可读性(而不是try-catch-finally)。 - 现在检查是否响应
IsSuccessStatusCode
发现问题(为此创建了另一个问题 - Timeouts in Xamarin HTTP requests):
有趣的是,当一个主机无法访问(例如,离线本地服务器)时,GetAsync 之后什么都没有发生。 几分钟后(大约 3 分钟)一个System.Net.WebException 被抛出,说Error: ConnectFailure (Connection timed out)。内部异常是System.Net.Sockets.SocketsException(此处为完整日志:http://pastebin.com/MzHyp2FM)。
我尝试将client.Timeout 设置为 5 秒但这似乎不起作用。
但可能是 Xamarin 错误 (https://forums.xamarin.com/discussion/5941/system-net-http-httpclient-timeout-seems-to-be-ignored)。
不管怎样,cancellationToken 不应该在 10 秒后自动取消?
此超时问题发生在/何时:
- 脱机/无法访问的 IP 地址(在我的情况下是 请求离线本地服务器,例如 192.168.1.101:8080)(例如,GetAsync、SendAsync、GetResponseAsync)
代码适用于/何时:
- 没有 Internet 连接(抛出异常)
- DNS 无法解析 URL(引发异常)
提供了一个有效的 URL(正常运行)
请求来自桌面客户端(例如 WPF),如果 IP 离线/无法访问它会很快抛出 4 个异常(否 可以建立连接,因为目标机器主动拒绝 它)
结论
- Xamarin 似乎在这些请求中有一些错误(超时在 至少?),因为它们没有给出预期的结果,否则在 桌面应用程序。
【问题讨论】:
-
HttpClient.Timeout和CancellationToken应该都可以正常工作。 Xamarin 最有可能出现的错误(我见过 很多 具有异步和并行支持)。 Xamarin 正在迁移到 .NET Core,这将极大地提高它们的可靠性和正确性。国际海事组织。 :) -
感谢@StephenCleary 光临!不幸的是,情况似乎如此(自 2012 年或 2013 年以来?)。还有什么我可以改进的吗?我的第一个问题怎么样?
-
不,我不会使用
Task.Run进行 JSON 反序列化。除非您的对象 大量,否则它们应该快速反序列化,即使它们 大量,Task.Run也属于 UI 级别,而不是WebUtilities模块。 -
@StephenCleary 完全有道理。我问是因为我看到了一些使用 Task.RunEx 进行 JSON 反序列化的示例。仅供参考,刚刚用一些测试结果更新了主帖......似乎这确实是 Xamarin 中的一个错误,因为它在桌面客户端应用程序中完美运行。我真的很难过...但很高兴代码有效。我能做什么?
-
当然,只是关于 Xamarin HTTP 请求的超时。
标签: c# asynchronous xamarin.android