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; } } }
理论上,如果在ct
和request.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