C# 异步下载和反序列化
晚上好, 我正在尝试使用异步编程来优化一些代码,我想知道下面的代码是否编写得很好,以及是否有任何改进的方法 其目的是从给定的URL下载一些JSON,并将其反序列化为对象 我有三个(现在是四个)问题(和一个在文章末尾描述的问题):C# 异步下载和反序列化,c#,asynchronous,xamarin.android,C#,Asynchronous,Xamarin.android,晚上好, 我正在尝试使用异步编程来优化一些代码,我想知道下面的代码是否编写得很好,以及是否有任何改进的方法 其目的是从给定的URL下载一些JSON,并将其反序列化为对象 我有三个(现在是四个)问题(和一个在文章末尾描述的问题): 我应该使用TaskEx.Run来运行吗 Newtonsoft.Json.JsonConvert.DeserializeObject 有没有什么好办法(不检查对象属性)来 知道是否成功创建了rootObject 我是否应该在某处检查是否有人要求取消 (新问题)我是否应该
- 我应该使用
来运行吗TaskEx.Run
Newtonsoft.Json.JsonConvert.DeserializeObject
- 有没有什么好办法(不检查对象属性)来
知道是否成功创建了
rootObject
- 我是否应该在某处检查是否有人要求取消
- (新问题)我是否应该在执行新请求之前取消PendingRequests李>
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();
}
}
内部静态类WebUtilities
{
///
///下载给定url的页面
///
///从中下载页面的url
///取消下载的令牌
///页面内容
内部静态异步任务下载StringAsync(字符串url,CancellationToken CancellationToken)
{
尝试
{
//创建Http客户机并在抛出异常的情况下处置它(与使用finally语句相同)
使用(var client=new HttpClient(){Timeout=TimeSpan.fromsons(5)})
{
//我应该一直这样做吗?
client.CancelPendingRequests();
//请求并在完成后处理
使用(var response=await client.GetAsync(url,cancellationToken).ConfigureAwait(false))
{
//如果响应成功(否则将返回null)
if(响应。IsSuccessStatusCode)
{
//返回其内容
返回wait response.Content.ReadAsStringAsync().configurewait(false);
}
}
}
}
当(ex是System.Net.Sockets.SocketException||时捕获(异常ex)
ex为无效操作例外| |
ex是操作取消异常||
ex是System.Net.Http.HttpRequestException)
{
WriteLine(“DownloadStringAsync任务已取消”);
WriteLine(例如消息);
返回null;
}
//如果响应不成功,则返回null
返回null;
}
///
///从给定url下载Json并尝试将其反序列化为给定引用类型(类)
///
///要反序列化到的类
///从中下载json的url
///取消下载的令牌
///反序列化的对象
内部静态异步任务下载JSONAND反序列化(字符串url,CancellationToken CancellationToken),其中T:class,new()
{
//从给定的url下载json
var jsonResponse=await DownloadStringAsync(url,cancellationToken);
//如果响应无效,则无需进一步说明
if(string.IsNullOrEmpty(jsonResponse))
//返回默认构造函数实例
返回新的T();
//尝试反序列化
尝试
{
//将json数据反序列化到给定的.NET对象
//我应该使用TaskEx。在这里运行吗?
返回Newtonsoft.Json.JsonConvert.DeserializeObject(jsonResponse);
}
捕获(异常ex)时(ex是JsonException)
{
WriteLine(“将json反序列化为给定引用类型时出错。”);
WriteLine(例如消息);
}
//返回默认构造函数实例
返回新的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?
}
内部静态异步任务CallAsync()
{
RootObject root=null;
使用(var cts=new CancellationTokenSource(TimeSpan.FromSeconds(10)))
{
var-token=cts.token;
root=await WebUtilities.DownloadJsonAndDeserialize(URL,令牌)。ConfigureAwait(false);
}
//对rootObject执行一些操作
//在继续之前,有什么好方法可以知道是否成功创建了rootObject?
}
你会改变什么?为什么
谢谢
编辑:
- @codran建议-每个
现在都在使用wait
configurewait(false)
现在已被处置(使用语句)CancellationTokenSource
- @codran建议-一些异常现在已被过滤
现在在一个DownloadStringAsync
块以提高可读性(而不是try catch
)try catch finally
- 现在检查响应是否
IsSuccessStatusCode
GetAsync
之后不会发生任何事情几分钟后(大约3分钟)抛出一个System.Net.WebException
,说错误:连接失败(连接超时)。内部异常是System.Net.Sockets.SocketsException
(此处的完整日志:)
我已尝试将客户端.Timeout
设置为5秒,但似乎不起作用
不过,可能是Xamarin的bug()
无论哪种方式,都不应该在10秒后自动取消cancellationToken
此超时问题在以下时间发生:
- 脱机/无法访问的IP地址(在我的示例中为
请求脱机本地服务器(如192.168.1.101:8080)(如Ge
// Creating and disposing HttpClient is unnecessary // if you are going to use it multiple time // reusing HttpClient improves performance !! // do not worry about memory leak, HttpClient // is designed to close resources used in single "SendAsync" // method call private static HttpClient client; public Task<T> DownloadAs<T>(string url){ // I know I am firing this on another thread // to keep UI free from any smallest task like // preparing httpclient, setting headers // checking for result or anything..., why do that on // UI Thread? // this helps us using excessive logging required for // debugging and diagnostics return Task.Run(async ()=> { // following parses url and makes sure // it is a valid url // there is no need for this to be done on // UI thread Uri uri = new Uri(url); HttpRequestMessage request = new HttpRequestMessage(uri,HttpMethod.Get); // do some checks, set some headers... // secrete code !!! var response = await client.SendAsync(request, HttpCompletionOption.ReadHeaders); string content = await response.Content.ReadAsStringAsync(); if(((int)response.StatusCode)>300){ // it is good to receive error text details // not just reason phrase throw new InvalidOperationException(response.ReasonPhrase + "\r\n" + content); } return JsonConvert.DeserializeObject<T>(content); }); }