Lorsqu'un utilisateur charge une page, il effectue une ou plusieurs requêtes ajax, qui atteignent les contrôleurs ASP.NET Web API 2. Si l'utilisateur accède à une autre page, avant que ces requêtes ajax ne soient terminées, les requêtes sont annulées par le navigateur. Notre ELMAH HttpModule enregistre ensuite deux erreurs pour chaque requête annulée:
Erreur 1:
System.Threading.Tasks.TaskCanceledException: A task was canceled.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Controllers.ExceptionFilterResult.<ExecuteAsync>d__0.MoveNext()
Erreur 2:
System.OperationCanceledException: The operation was canceled.
at System.Threading.CancellationToken.ThrowIfCancellationRequested()
at System.Web.Http.WebHost.HttpControllerHandler.<WriteBufferedResponseContentAsync>d__1b.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.WebHost.HttpControllerHandler.<CopyResponseAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.WebHost.HttpControllerHandler.<ProcessRequestAsyncCore>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.TaskAsyncHelper.EndTask(IAsyncResult ar)
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
En regardant le stacktrace, je vois que l'exception est levée à partir d'ici: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# L413
Ma question est la suivante: comment puis-je gérer et ignorer ces exceptions?
Cela semble être en dehors du code utilisateur ...
Remarques:
- J'utilise l'API Web ASP.NET 2
- Les points de terminaison de l'API Web sont un mélange de méthodes asynchrones et non asynchrones.
- Peu importe où j'ajoute la journalisation des erreurs, je ne parviens pas à détecter l'exception dans le code utilisateur
- Global.asax
Applicaiton_Error
TaskScheduler.UnobservedTaskException
- Filtrage d'erreurs ELMAH
void ErrorLog_Filtering
( https://code.google.com/p/elmah/wiki/ErrorFiltering )
- Global.asax
asp.net
iis
asp.net-web-api
task-parallel-library
async-await
Bates Westmoreland
la source
la source
Réponses:
Il s'agit d'un bogue dans l'API Web ASP.NET 2 et malheureusement, je ne pense pas qu'il existe une solution de contournement qui réussira toujours. Nous avons déposé un bug pour le corriger de notre côté.
En fin de compte, le problème est que nous renvoyons une tâche annulée à ASP.NET dans ce cas, et ASP.NET traite une tâche annulée comme une exception non gérée (il enregistre le problème dans le journal des événements d'application).
En attendant, vous pouvez essayer quelque chose comme le code ci-dessous. Il ajoute un gestionnaire de messages de niveau supérieur qui supprime le contenu lorsque le jeton d'annulation se déclenche. Si la réponse n'a pas de contenu, le bogue ne devrait pas être déclenché. Il y a encore une petite possibilité que cela puisse arriver, car le client pourrait se déconnecter juste après que le gestionnaire de messages ait vérifié le jeton d'annulation, mais avant que le code de l'API Web de niveau supérieur ne fasse la même vérification. Mais je pense que cela aidera dans la plupart des cas.
David
la source
SendAsync
(vous pouvez simuler cela en maintenant enfoncéeF5
dans le navigateur une URL qui envoie des demandes à votre API. J'ai également résolu ce problème ajouter leif (cancellationToken.IsCancellationRequested)
coche au-dessus de l'appel àSendAsync
. Désormais, les exceptions n'apparaissent plus lorsque le navigateur annule rapidement les demandes.Lors de l'implémentation d'un enregistreur d'exceptions pour WebApi, il est recommandé d'étendre la
System.Web.Http.ExceptionHandling.ExceptionLogger
classe plutôt que de créer un ExceptionFilter. Les internes WebApi n'appelleront pas la méthode Log des ExceptionLoggers pour les demandes annulées (cependant, les filtres d'exception les obtiendront). C'est par conception.la source
Voici une autre solution de contournement pour ce problème. Ajoutez simplement un middleware OWIN personnalisé au début du pipeline OWIN qui intercepte
OperationCanceledException
:la source
Vous pouvez essayer de modifier le comportement de gestion des exceptions de tâche TPL par défaut via
web.config
:Ensuite, ayez une
static
classe (avec unstatic
constructeur) dans votre application Web, qui géreraitAppDomain.UnhandledException
.Cependant, il semble que cette exception soit en fait gérée quelque part dans le runtime de l'API Web ASP.NET , avant même que vous n'ayez une chance de la gérer avec votre code.
Dans ce cas, vous devriez pouvoir l'attraper comme une exception de 1ère chance, avec
AppDomain.CurrentDomain.FirstChanceException
, voici comment . Je comprends que ce n'est peut-être pas ce que vous recherchez.la source
FirstChanceException
? Avez-vous essayé de le gérer avec une classe statique qui persiste dans les requêtes HTTP?AppDomain.UnhandledException
ouAppDomain.CurrentDomain.FirstChanceException
peut me permettre d'inspecter l'exception, mais pas d'attraper et d'ignorer. Je n'ai pas vu de moyen de marquer ces exceptions comme étant gérées en utilisant l'une ou l'autre de ces approches. Corrigez-moi si je me trompe.J'obtiens parfois les mêmes 2 exceptions dans mon application Web API 2, mais je peux les attraper avec la
Application_Error
méthodeGlobal.asax.cs
et en utilisant un filtre d'exception générique .Ce qui est drôle, c'est que je préfère ne pas attraper ces exceptions, car je consigne toujours toutes les exceptions non gérées qui peuvent faire planter l'application (ces 2, cependant, ne sont pas pertinentes pour moi et apparemment ne se bloquent pas ou du moins ne devraient pas planter il, mais je peux me tromper). Je soupçonne que ces erreurs apparaissent en raison de l'expiration du délai d'expiration ou d'une annulation explicite du client, mais je me serais attendu à ce qu'elles soient traitées à l'intérieur du framework ASP.NET et non propagées en dehors de celui-ci comme des exceptions non gérées.
la source
WinHTTP
API, pas à partir d'un navigateur.J'ai trouvé un peu plus de détails sur cette erreur. Il y a 2 exceptions possibles qui peuvent se produire:
OperationCanceledException
TaskCanceledException
Le premier se produit si la connexion est interrompue pendant que votre code dans le contrôleur s'exécute (ou éventuellement un code système autour de cela également). Alors que le second se produit si la connexion est abandonnée alors que l'exécution est à l'intérieur d'un attribut (par exemple
AuthorizeAttribute
).Ainsi, la solution de contournement fournie permet d'atténuer partiellement la première exception, elle ne fait rien pour aider avec la seconde. Dans ce dernier cas, le
TaskCanceledException
se produit pendant l'base.SendAsync
appel lui-même plutôt que le jeton d'annulation étant défini sur true.Je peux voir deux façons de résoudre ces problèmes:
TaskCanceledException
que nous ignorons soit celui que nous voulons enregistrer.La seule façon dont j'ai compris que nous pouvons essayer de localiser les mauvaises exceptions est de vérifier si stacktrace contient des éléments Asp.Net. Cela ne semble pas très robuste cependant.
PS Voici comment je filtre ces erreurs:
la source
response
variable à l'intérieur de l'essai et la renvoyez en dehors de l'essai. Cela ne peut pas fonctionner, n'est-ce pas? Où utilisez-vous également l'exception IsAspNetBugException?Nous avons reçu la même exception, nous avons essayé d'utiliser la solution de contournement de @ dmatson, mais nous obtiendrions toujours une exception. Nous l'avons traité jusqu'à récemment. Nous avons remarqué que certains journaux Windows augmentaient à un rythme alarmant.
Fichiers d'erreur situés dans: C: \ Windows \ System32 \ LogFiles \ HTTPERR
La plupart des erreurs concernaient toutes "Timer_ConnectionIdle". J'ai cherché partout et il semblait que même si l'appel de l'API Web était terminé, la connexion persistait encore pendant deux minutes après la connexion d'origine.
J'ai alors pensé que nous devrions essayer de fermer la connexion dans la réponse et voir ce qui se passe.
J'ai ajouté
response.Headers.ConnectionClose = true;
au SendAsync MessageHandler et d'après ce que je peux dire, les clients ferment les connexions et nous ne rencontrons plus le problème.Je sais que ce n'est pas la meilleure solution, mais cela fonctionne dans notre cas. Je suis également presque sûr que ce n'est pas quelque chose que vous voudriez faire si votre API reçoit plusieurs appels du même client dos à dos.
la source