GetResponseAsync不接受cancellationToken

似乎GetResponseAsync在Async / Await中不接受cancellationToken。 所以问题是我如何取消以下程序,只要我需要从响应中收集Cookie:

using (HttpWebResponse response = (HttpWebResponse) await request.GetResponseAsync()) { cookies.Add(response.Cookies); } 

实现上述目标的替代代码也是受欢迎的。

这样的东西应该工作(未经测试):

 public static class Extensions { public static async Task GetResponseAsync(this HttpWebRequest request, CancellationToken ct) { using (ct.Register(() => request.Abort(), useSynchronizationContext: false)) { var response = await request.GetResponseAsync(); ct.ThrowIfCancellationRequested(); return (HttpWebResponse)response; } } } 

理论上,如果在ctrequest.Abort上请求取消,则await request.GetResponseAsync()应抛出WebException 。 尽管如此,在使用结果时明确检查取消以减轻竞争条件总是一个好主意,所以我调用ct.ThrowIfCancellationRequested()

另外,我假设request.Abort是线程安全的(可以从任何线程调用),所以我使用useSynchronizationContext: false (我还没有validation过)。

[UPDATED]解决OP关于如何区分由取消引起的WebException和任何其他错误的评论。 这是如何完成的,因此在取消时将正确抛出TaskCanceledException (派生自OperationCanceledException ):

 public static class Extensions { public static async Task GetResponseAsync(this HttpWebRequest request, CancellationToken ct) { using (ct.Register(() => request.Abort(), useSynchronizationContext: false)) { try { var response = await request.GetResponseAsync(); return (HttpWebResponse)response; } catch (WebException ex) { // WebException is thrown when request.Abort() is called, // but there may be many other reasons, // propagate the WebException to the caller correctly if (ct.IsCancellationRequested) { // the WebException will be available as Exception.InnerException throw new OperationCanceledException(ex.Message, ex, ct); } // cancellation hasn't been requested, rethrow the original WebException throw; } } } } 
 public static async Task WithCancellation(this Task task, CancellationToken cancellationToken, Action action, bool useSynchronizationContext = true) { using (cancellationToken.Register(action, useSynchronizationContext)) { try { return await task; } catch (Exception ex) { if (cancellationToken.IsCancellationRequested) { // the Exception will be available as Exception.InnerException throw new OperationCanceledException(ex.Message, ex, cancellationToken); } // cancellation hasn't been requested, rethrow the original Exception throw; } } } 

现在,您可以在任何可取消的异步方法上使用取消令牌。 例如WebRequest.GetResponseAsync:

 var request = (HttpWebRequest)WebRequest.Create(url); using (var response = await request.GetResponseAsync()) { . . . } 

会变成:

 var request = (HttpWebRequest)WebRequest.Create(url); using (WebResponse response = await request.GetResponseAsync().WithCancellation(CancellationToken.None, request.Abort, true)) { . . . } 

请参见示例http://pastebin.com/KauKE0rW