Quando um usuário carrega uma página, ele faz uma ou mais solicitações ajax, que atingem os controladores ASP.NET Web API 2. Se o usuário navegar para outra página, antes que essas solicitações ajax sejam concluídas, as solicitações são canceladas pelo navegador. Nosso ELMAH HttpModule então registra dois erros para cada solicitação cancelada:
Erro 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()
Erro 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)
Olhando para o stacktrace, vejo que a exceção está sendo lançada a partir daqui: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# L413
Minha pergunta é: como posso lidar e ignorar essas exceções?
Parece estar fora do código do usuário ...
Notas:
- Estou usando ASP.NET Web API 2
- Os endpoints da API da Web são uma mistura de métodos assíncronos e não assíncronos.
- Não importa onde eu adiciono o registro de erros, não consigo capturar a exceção no código do usuário
- Global.asax
Applicaiton_Error
TaskScheduler.UnobservedTaskException
- ELMAH Error Filtering
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
fonte
fonte
Respostas:
Este é um bug no ASP.NET Web API 2 e, infelizmente, não acho que haja uma solução alternativa que sempre terá sucesso. Arquivamos um bug para consertá-lo do nosso lado.
Em última análise, o problema é que retornamos uma tarefa cancelada ao ASP.NET neste caso, e o ASP.NET trata a tarefa cancelada como uma exceção não tratada (ele registra o problema no log de eventos do aplicativo).
Enquanto isso, você pode tentar algo como o código abaixo. Ele adiciona um manipulador de mensagens de nível superior que remove o conteúdo quando o token de cancelamento é disparado. Se a resposta não tiver conteúdo, o bug não deve ser acionado. Ainda há uma pequena possibilidade de que isso aconteça, porque o cliente pode se desconectar logo após o manipulador de mensagens verificar o token de cancelamento, mas antes que o código da API da Web de nível superior faça a mesma verificação. Mas acho que vai ajudar na maioria dos casos.
David
fonte
SendAsync
(você pode simular isso mantendo pressionadoF5
no navegador um url que faz solicitações à sua Api. Resolvi esse problema por também adicionando aif (cancellationToken.IsCancellationRequested)
marca de verificação acima da chamada paraSendAsync
. Agora as exceções não aparecem mais quando o navegador cancela rapidamente as solicitações.Ao implementar um registrador de exceções para WebApi, é recomendável estender a
System.Web.Http.ExceptionHandling.ExceptionLogger
classe em vez de criar um ExceptionFilter. Os internos WebApi não chamarão o método Log de ExceptionLoggers para solicitações canceladas (no entanto, os filtros de exceção irão obtê-los). Isso ocorre por design.fonte
Aqui está uma outra solução alternativa para esse problema. Basta adicionar um middleware OWIN personalizado no início do pipeline OWIN que captura
OperationCanceledException
:fonte
Você pode tentar alterar o comportamento de manipulação de exceção de tarefa TPL padrão por meio de
web.config
:Em seguida, tenha uma
static
classe (com umstatic
construtor) em seu aplicativo da web, que manipulariaAppDomain.UnhandledException
.No entanto, parece que essa exceção está realmente sendo tratada em algum lugar dentro do tempo de execução da API da Web do ASP.NET , antes mesmo de você ter a chance de manipulá-la com seu código.
Nesse caso, você deve ser capaz de capturá-lo como uma exceção de 1ª chance, com
AppDomain.CurrentDomain.FirstChanceException
, aqui está como . Eu entendo que pode não ser o que você está procurando.fonte
FirstChanceException
? Você já tentou lidar com isso com uma classe estática que persiste em solicitações HTTP?AppDomain.UnhandledException
ouAppDomain.CurrentDomain.FirstChanceException
pode me permitir inspecionar a exceção, mas não capturar e ignorar. Não vi uma maneira de marcar essas exceções como tratadas usando qualquer uma dessas abordagens. Corrija-me se eu estiver errada.Às vezes, recebo as mesmas 2 exceções em meu aplicativo Web API 2, mas posso capturá-las com o
Application_Error
método emGlobal.asax.cs
e usando um filtro de exceção genérico .O engraçado é, porém, eu prefiro não pegar essas exceções, porque eu sempre registro todas as exceções não tratadas que podem travar o aplicativo (essas 2, no entanto, são irrelevantes para mim e aparentemente não travam ou pelo menos não deveriam travar , mas posso estar errado). Suspeito que esses erros apareçam devido a alguma expiração de tempo limite ou cancelamento explícito do cliente, mas eu esperava que eles fossem tratados dentro da estrutura ASP.NET e não propagados fora dela como exceções não tratadas.
fonte
WinHTTP
API, não de um navegador.Eu encontrei um pouco mais de detalhes sobre este erro. Existem 2 exceções possíveis que podem acontecer:
OperationCanceledException
TaskCanceledException
O primeiro acontece se a conexão for interrompida enquanto seu código no controlador é executado (ou possivelmente algum código de sistema ao redor disso também). Já o segundo ocorre se a conexão for interrompida enquanto a execução estiver dentro de um atributo (por exemplo
AuthorizeAttribute
).Portanto, a solução alternativa fornecida ajuda a mitigar parcialmente a primeira exceção, mas não ajuda em nada com a segunda. No último caso,
TaskCanceledException
ocorre durante abase.SendAsync
própria chamada, em vez de o token de cancelamento ser definido como verdadeiro.Posso ver duas maneiras de resolver isso:
TaskCanceledException
ignorarmos ser aquele que desejamos registrar.A única maneira que descobri que podemos tentar localizar as exceções erradas é verificando se stacktrace contém algum material Asp.Net. Não parece muito robusto.
PS É assim que eu filtro esses erros:
fonte
response
variável dentro do try e a retorna fora do try. Isso não pode funcionar, pode? Além disso, onde você usa o IsAspNetBugException?Temos recebido a mesma exceção, tentamos usar a solução alternativa de @dmatson, mas ainda obteríamos alguma exceção. Nós lidamos com isso até recentemente. Percebemos alguns logs do Windows crescendo a uma taxa alarmante.
Arquivos de erro localizados em: C: \ Windows \ System32 \ LogFiles \ HTTPERR
A maioria dos erros foram todos para "Timer_ConnectionIdle". Eu procurei e parecia que, embora a chamada da API da web tivesse sido concluída, a conexão ainda persistia por dois minutos após a conexão original.
Então percebi que deveríamos tentar fechar a conexão na resposta e ver o que acontece.
Eu adicionei
response.Headers.ConnectionClose = true;
ao SendAsync MessageHandler e pelo que posso dizer, os clientes estão fechando as conexões e não estamos tendo mais o problema.Sei que não é a melhor solução, mas funciona no nosso caso. Também estou certo de que, em termos de desempenho, isso não é algo que você gostaria de fazer se sua API estiver recebendo várias chamadas do mesmo cliente consecutivas.
fonte