Gdy użytkownik ładuje stronę, wysyła jedno lub więcej żądań ajax, które trafiają do kontrolerów ASP.NET Web API 2. Jeśli użytkownik przejdzie na inną stronę, przed zakończeniem tych żądań ajax, żądania są anulowane przez przeglądarkę. Nasz ELMAH HttpModule następnie rejestruje dwa błędy dla każdego anulowanego żądania:
Błąd 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()
Błąd 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)
Patrząc na stacktrace, widzę, że wyjątek jest wyrzucany stąd: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# L413
Moje pytanie brzmi: jak mogę obsłużyć i zignorować te wyjątki?
Wygląda na to, że jest poza kodem użytkownika ...
Uwagi:
- Używam ASP.NET Web API 2
- Punkty końcowe interfejsu API sieci Web to połączenie metod asynchronicznych i innych niż asynchroniczne.
- Bez względu na to, gdzie dodam rejestrowanie błędów, nie mogę złapać wyjątku w kodzie użytkownika
- Global.asax
Applicaiton_Error
TaskScheduler.UnobservedTaskException
- Filtrowanie błędów 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
źródło
źródło
Odpowiedzi:
Jest to błąd w ASP.NET Web API 2 i niestety nie sądzę, aby można było obejść ten problem, który zawsze się powiedzie. Zgłosiliśmy błąd, aby go naprawić po naszej stronie.
Ostatecznie problem polega na tym, że w tym przypadku zwracamy anulowane zadanie do ASP.NET, a ASP.NET traktuje anulowane zadanie jak nieobsługiwany wyjątek (rejestruje problem w dzienniku zdarzeń aplikacji).
W międzyczasie możesz spróbować czegoś podobnego do poniższego kodu. Dodaje moduł obsługi komunikatów najwyższego poziomu, który usuwa zawartość po uruchomieniu tokenu anulowania. Jeśli odpowiedź nie zawiera treści, błąd nie powinien zostać wywołany. Nadal istnieje niewielka możliwość, że może się to zdarzyć, ponieważ klient może się rozłączyć zaraz po sprawdzeniu tokenu anulowania przez program obsługi komunikatów, ale zanim kod interfejsu API sieci Web wyższego poziomu wykona to samo sprawdzenie. Ale myślę, że w większości przypadków pomoże.
David
źródło
SendAsync
(możesz to zasymulować, przytrzymującF5
w przeglądarce adres URL, który wysyła żądania do Twojego interfejsu API. Rozwiązałem ten problem również dodanieif (cancellationToken.IsCancellationRequested)
czeku nad wezwaniem doSendAsync
. Teraz wyjątki nie są już wyświetlane, gdy przeglądarka szybko anuluje żądania.Podczas implementowania rejestratora wyjątków dla WebApi zaleca się rozszerzenie
System.Web.Http.ExceptionHandling.ExceptionLogger
klasy zamiast tworzenia ExceptionFilter. Elementy wewnętrzne WebApi nie będą wywoływać metody Log programu ExceptionLoggers w przypadku anulowanych żądań (jednak filtry wyjątków będą je pobierać). Jest to zgodne z projektem.źródło
Oto inne obejście tego problemu. Po prostu dodaj niestandardowe oprogramowanie pośredniczące OWIN na początku potoku OWIN, które przechwytuje
OperationCanceledException
:źródło
Możesz spróbować zmienić domyślne zachowanie obsługi wyjątków zadania TPL poprzez
web.config
:Następnie miej
static
klasę (zstatic
konstruktorem) w swojej aplikacji internetowej, która będzie obsługiwaćAppDomain.UnhandledException
.Wygląda jednak na to, że ten wyjątek jest faktycznie obsługiwany gdzieś w środowisku wykonawczym ASP.NET Web API , zanim jeszcze będziesz miał szansę obsłużyć go za pomocą kodu.
W takim przypadku powinieneś być w stanie złapać go jako wyjątek pierwszej szansy, za pomocą
AppDomain.CurrentDomain.FirstChanceException
, oto jak . Rozumiem, że może to nie być to, czego szukasz.źródło
FirstChanceException
? Czy próbowałeś poradzić sobie z tym za pomocą klasy statycznej, która utrzymuje się w żądaniach HTTP?AppDomain.UnhandledException
lubAppDomain.CurrentDomain.FirstChanceException
może pozwolić mi na sprawdzenie wyjątku, ale nie na przechwytywanie i ignorowanie. Nie widziałem sposobu, aby oznaczyć te wyjątki jako obsługiwane przy użyciu któregokolwiek z tych podejść. Popraw mnie, jeśli się mylę.Czasami dostaję te same 2 wyjątki w mojej aplikacji Web API 2, jednak mogę je złapać za pomocą
Application_Error
metody wiGlobal.asax.cs
używając ogólnego filtru wyjątków .Zabawne jest jednak to, że wolę nie łapać tych wyjątków, ponieważ zawsze rejestruję wszystkie nieobsłużone wyjątki, które mogą spowodować awarię aplikacji (te 2 są jednak dla mnie nieistotne i najwyraźniej nie mają lub przynajmniej nie powinny się zawieszać to, ale mogę się mylić). Podejrzewam, że te błędy pojawiają się z powodu upływu limitu czasu lub jawnego anulowania przez klienta, ale spodziewałbym się, że będą traktowane wewnątrz platformy ASP.NET i nie będą propagowane poza nią jako nieobsługiwane wyjątki.
źródło
WinHTTP
interfejsu API, a nie przeglądarki.Znalazłem trochę więcej szczegółów na temat tego błędu. Mogą wystąpić 2 możliwe wyjątki:
OperationCanceledException
TaskCanceledException
Pierwsza z nich ma miejsce, gdy połączenie zostanie zerwane podczas wykonywania kodu w kontrolerze (lub prawdopodobnie również kod systemowy wokół tego). Podczas gdy drugi ma miejsce, jeśli połączenie zostanie zerwane, gdy wykonanie znajduje się wewnątrz atrybutu (np
AuthorizeAttribute
.).Tak więc podane obejście pomaga częściowo złagodzić pierwszy wyjątek, a nie pomaga w przypadku drugiego. W tym drugim przypadku
TaskCanceledException
występuje podczasbase.SendAsync
samego wywołania, a token anulowania jest ustawiony na wartość true.Widzę dwa sposoby rozwiązania tego problemu:
TaskCanceledException
że to , co zignorujemy, będzie tym, które chcemy zarejestrować.Jedynym sposobem, w jaki się zorientowałem, że możemy spróbować wskazać niewłaściwe wyjątki, jest sprawdzenie, czy stacktrace zawiera jakieś elementy Asp.Net. Nie wydaje się jednak zbyt mocny.
PS Tak odfiltrowuję te błędy:
źródło
response
zmienną wewnątrz try i zwracasz ją poza try. To niemożliwe, prawda? Gdzie również używasz wyjątku IsAspNetBugException?Otrzymaliśmy ten sam wyjątek, próbowaliśmy zastosować obejście @ dmatson, ale mimo to otrzymaliśmy jakiś wyjątek. Zajmowaliśmy się tym do niedawna. Zauważyliśmy, że niektóre dzienniki systemu Windows rosną w alarmującym tempie.
Pliki błędów zlokalizowane w: C: \ Windows \ System32 \ LogFiles \ HTTPERR
Większość błędów dotyczyła „Timer_ConnectionIdle”. Szukałem dookoła i wydawało się, że chociaż wywołanie interfejsu API sieci Web zostało zakończone, połączenie nadal trwało przez dwie minuty po pierwotnym połączeniu.
Wtedy doszedłem do wniosku, że powinniśmy spróbować zamknąć połączenie w odpowiedzi i zobaczyć, co się stanie.
Dodałem
response.Headers.ConnectionClose = true;
do SendAsync MessageHandler iz tego, co mogę powiedzieć, klienci zamykają połączenia i już nie mamy problemu.Wiem, że to nie jest najlepsze rozwiązanie, ale w naszym przypadku działa. Jestem też prawie pewien, że pod względem wydajności nie jest to coś, co chciałbyś zrobić, jeśli twoje API otrzymuje wiele wywołań od tego samego klienta z powrotem do tyłu.
źródło