如何强制在连续任务中观察到任务异常?
我有一个执行 HttpWebRequest
的任务,使用
Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)
它显然会失败并出现 WebException
。对于调用者,我想返回一个 Task
,其中 HttpResult
是封装响应(或不封装响应)的帮助程序类型。在这种情况下,4xx 或 5xx 响应不是例外。
因此,我在请求任务中附加了两个延续。一种使用 TaskContinuationOptions
OnlyOnRanToCompletion
,另一种使用 OnlyOnOnFaulted
。然后将整个内容包装在 Task
中,以获取任一延续完成的结果。
三个子任务(请求加上两个延续)中的每一个都是使用 AttachedToParent
选项创建的。
但是当调用者等待返回的外部任务时,如果请求失败,则会抛出 AggregateException 。
我想在 on failed 延续中观察 WebException
,以便客户端代码可以只查看结果。在错误继续抛出中添加 Wait
,但是围绕此问题的 try-catch 没有帮助。查看 Exception 属性(如“使用 Task.Exception 属性观察异常”部分)也没有提示 此处)。
我可以安装一个 UnobservedTaskException 事件处理程序来过滤,但由于该事件没有提供到故障任务的直接链接,因此这可能会在应用程序的这一部分之外进行交互,并且是一个大锤敲碎坚果的情况。
给定一个有故障的 Task
实例,是否有任何方法将其标记为“故障已处理”?
简化的代码:
public static Task<HttpResult> Start(Uri url) {
var webReq = BuildHttpWebRequest(url);
var result = new HttpResult();
var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
var tRequest = Task<WebResponse>.Factory.FromAsync(
webReq.BeginGetResponse,
webReq.EndGetResponse,
null, TaskCreationOptions.AttachedToParent);
var tError = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestError(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnFaulted);
var tSuccess = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestSuccess(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnRanToCompletion);
return result;
});
return taskOuter;
}
with:
private static HttpDownloaderResult HandleWebRequestError(
Task<WebResponse> respTask,
HttpResult result) {
Debug.Assert(respTask.Status == TaskStatus.Faulted);
Debug.Assert(respTask.Exception.InnerException is WebException);
// Try and observe the fault: Doesn't help.
try {
respTask.Wait();
} catch (AggregateException e) {
Log("HandleWebRequestError: waiting on antecedent task threw inner: "
+ e.InnerException.Message);
}
// ... populate result with details of the failure for the client ...
return result;
}
(HandleWebRequestSuccess
最终将派生出进一步的任务来获取响应的内容...)
客户端应该能够等待任务,然后查看其结果,而不需要它由于预期且已处理的错误而抛出。
I have a task to perform an HttpWebRequest
using
Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)
which can obviously fail with a WebException
. To the caller I want to return a Task<HttpResult>
where HttpResult
is a helper type to encapsulate the response (or not). In this case a 4xx or 5xx response is not an exception.
Therefore I've attached two continuations to the request task. One with TaskContinuationOptions
OnlyOnRanToCompletion
and the other with OnlyOnOnFaulted
. And then wrapped the whole thing in a Task<HttpResult>
to pick up the one result whichever continuation completes.
Each of the three child tasks (request plus two continuations) is created with the AttachedToParent
option.
But when the caller waits on the returned outer task, an AggregateException
is thrown is the request failed.
I want to, in the on faulted continuation, observe the WebException
so the client code can just look at the result. Adding a Wait
in the on fault continuation throws, but a try-catch around this doesn't help. Nor does looking at the Exception
property (as section "Observing Exceptions By Using the Task.Exception Property" hints here).
I could install a UnobservedTaskException
event handler to filter, but as the event offers no direct link to the faulted task this will likely interact outside this part of the application and is a case of a sledgehammer to crack a nut.
Given an instance of a faulted Task<T>
is there any means of flagging it as "fault handled"?
Simplified code:
public static Task<HttpResult> Start(Uri url) {
var webReq = BuildHttpWebRequest(url);
var result = new HttpResult();
var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
var tRequest = Task<WebResponse>.Factory.FromAsync(
webReq.BeginGetResponse,
webReq.EndGetResponse,
null, TaskCreationOptions.AttachedToParent);
var tError = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestError(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnFaulted);
var tSuccess = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestSuccess(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnRanToCompletion);
return result;
});
return taskOuter;
}
with:
private static HttpDownloaderResult HandleWebRequestError(
Task<WebResponse> respTask,
HttpResult result) {
Debug.Assert(respTask.Status == TaskStatus.Faulted);
Debug.Assert(respTask.Exception.InnerException is WebException);
// Try and observe the fault: Doesn't help.
try {
respTask.Wait();
} catch (AggregateException e) {
Log("HandleWebRequestError: waiting on antecedent task threw inner: "
+ e.InnerException.Message);
}
// ... populate result with details of the failure for the client ...
return result;
}
(HandleWebRequestSuccess
will eventually spin off further tasks to get the content of the response...)
The client should be able to wait on the task and then look at its result, without it throwing due to a fault that is expected and already handled.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
最后我采取了我能想到的最简单的路线:隐藏异常。这是可能的,因为
WebException
有一个属性Response
,它可以访问我想要的HttpWebResponse
:然后在延续任务。
In the end I took the simplest route I could think of: hide the exception. This is possible because
WebException
has a propertyResponse
which gives access to theHttpWebResponse
I want:And then handle errors, redirects and success responses in the continuation task.