브라우저가 요청을 취소 할 때 ASP.NET Web API OperationCanceledException


119

사용자가 페이지를로드하면 ASP.NET Web API 2 컨트롤러에 도달하는 하나 이상의 ajax 요청이 생성됩니다. 사용자가 다른 페이지로 이동하면 이러한 ajax 요청이 완료되기 전에 브라우저에서 요청을 취소합니다. 그런 다음 ELMAH HttpModule은 취소 된 각 요청에 대해 두 가지 오류를 기록합니다.

오류 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()

오류 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)

stacktrace를 살펴보면 https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs# 에서 예외가 발생하고 있음을 알 수 있습니다. L413

내 질문은 : 이러한 예외를 어떻게 처리하고 무시할 수 있습니까?

사용자 코드 외부에있는 것 같습니다 ...

노트:

  • ASP.NET Web API 2를 사용하고 있습니다.
  • Web API 끝점은 비동기 및 비 비동기 메서드의 혼합입니다.
  • 오류 로깅을 어디에 추가해도 사용자 코드에서 예외를 포착 할 수 없습니다.

1
Katana 라이브러리의 현재 버전에서도 동일한 예외 (TaskCanceledException 및 OperationCanceledException)를 보았습니다.
David McClelland

두 예외가 발생하는시기에 대한 자세한 내용을 발견하고이 해결 방법이 둘 중 하나에 대해서만 작동한다는 것을 알아 냈습니다. 여기에 몇 가지 세부 사항은 다음과 같습니다 stackoverflow.com/questions/22157596/...
일리아 Chernomordik

답변:


78

이것은 ASP.NET Web API 2의 버그이며 안타깝게도 항상 성공할 해결 방법이 없다고 생각합니다. 우리 측에서 버그 를 수정했습니다.

궁극적으로 문제는이 경우 취소 된 작업을 ASP.NET에 반환하고 ASP.NET은 취소 된 작업을 처리되지 않은 예외처럼 처리한다는 것입니다 (응용 프로그램 이벤트 로그에 문제를 기록함).

그 동안 아래 코드와 같은 것을 시도해 볼 수 있습니다. 취소 토큰이 실행될 때 콘텐츠를 제거하는 최상위 메시지 처리기를 추가합니다. 응답에 내용이 없으면 버그가 트리거되지 않아야합니다. 메시지 처리기가 취소 토큰을 확인한 직후 클라이언트가 연결을 끊을 수 있지만 상위 수준의 Web API 코드가 동일한 확인을 수행하기 전에 발생할 수있는 가능성은 여전히 ​​적습니다. 그러나 대부분의 경우 도움이 될 것이라고 생각합니다.

데이비드

config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());

class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        // Try to suppress response content when the cancellation token has fired; ASP.NET will log to the Application event log if there's content in this case.
        if (cancellationToken.IsCancellationRequested)
        {
            return new HttpResponseMessage(HttpStatusCode.InternalServerError);
        }

        return response;
    }
}

2
업데이트로 일부 요청을 캡처합니다. 우리는 여전히 로그에서 꽤 많은 것을 볼 수 있습니다. 해결 방법에 감사드립니다. 수정을 기대합니다.
Bates Westmoreland 2014

2
@KiranChalla-5.2.2로 업그레이드해도 여전히 이러한 오류가 있음을 확인할 수 있습니다.
KnightFox

2
여전히 오류가 발생합니다. 위의 제안, 다른 단서를 사용했습니다.
M2012

3
위의 권장 사항을 시도했을 때 요청이 전달되기 전에도 요청이 취소되었을 때 여전히 예외가 발생했습니다 SendAsync( F5Api에 요청하는 URL을 브라우저에서 길게 눌러이를 시뮬레이션 할 수 있습니다 .이 문제도 해결했습니다. 가산하지 if (cancellationToken.IsCancellationRequested)호출 위의 수표를 SendAsync브라우저가 신속하게 요청을 취소 할 때 이제 예외가 더 이상 표시됩니다..
seangwright

2
두 예외가 발생하는시기에 대한 자세한 내용을 발견하고이 해결 방법이 둘 중 하나에 대해서만 작동한다는 것을 알아 냈습니다. 여기에 몇 가지 세부 사항은 다음과 같습니다 stackoverflow.com/a/51514604/1671558
일리아 Chernomordik

17

WebApi에 대한 예외 로거를 구현할 때 System.Web.Http.ExceptionHandling.ExceptionLoggerExceptionFilter를 생성하는 대신 클래스 를 확장하는 것이 좋습니다 . WebApi 내부는 취소 된 요청에 대해 ExceptionLoggers의 Log 메서드를 호출하지 않습니다 (그러나 예외 필터는 요청을 가져옵니다). 이것은 의도적으로 설계된 것입니다.

HttpConfiguration.Services.Add(typeof(IExceptionLogger), myWebApiExceptionLogger); 

이 방법의 문제는 그것이 예외 핸들러로 전송되지 비록 오류가 여전히 Global.asax에 오류 처리에 ... 이벤트 팝업 있다는 것 같다
일리아 Chernomordik

14

이 문제에 대한 다른 해결 방법은 다음과 같습니다. 다음을 포착하는 OWIN 파이프 라인의 시작 부분에 사용자 지정 OWIN 미들웨어를 추가하기 만하면됩니다 OperationCanceledException.

#if !DEBUG
app.Use(async (ctx, next) =>
{
    try
    {
        await next();
    }
    catch (OperationCanceledException)
    {
    }
});
#endif

2
나는 OWIN 컨텍스트에서 주로이 오류가 더 나은이 하나의 목표를했다
NitinSingh

3

다음을 통해 기본 TPL 작업 예외 처리 동작 을 변경해 볼 수 있습니다 web.config.

<configuration> 
    <runtime> 
        <ThrowUnobservedTaskExceptions enabled="true"/> 
    </runtime> 
</configuration>

그런 다음 웹 앱에 생성자 가있는 static클래스를 가지고 .staticAppDomain.UnhandledException

그러나이 예외는 코드로 처리 할 기회도 갖기 전에 ASP.NET Web API 런타임 내부에서 실제로 처리되는 것으로 보입니다 .

이 경우에, 당신은과 더불어, 1 번째 예외로 잡을 수 있어야합니다 AppDomain.CurrentDomain.FirstChanceException, 여기에 방법이다 . 나는 이것이 당신이 찾고있는 것이 아닐 수도 있음을 이해합니다.


1
둘 다 예외 처리를 허용하지 않았습니다.
Bates Westmoreland 2014 년

@BatesWestmoreland, 심지어 FirstChanceException? HTTP 요청에서 지속되는 정적 클래스로 처리해 보셨습니까?
noseratio

2
내가 해결하려는 문제는 이러한 예외를 포착하고 무시합니다. 사용 AppDomain.UnhandledException또는 AppDomain.CurrentDomain.FirstChanceException나에게 예외를 검사,하지만 잡아 무시하도록 허용 할 수 있습니다. 이러한 접근 방식 중 하나를 사용하여 이러한 예외를 처리 한 것으로 표시하는 방법을 보지 못했습니다. 내가 틀렸다면 정정하십시오.
Bates Westmoreland 2014 년

2

Web API 2 응용 프로그램에서 때때로 동일한 2 개의 예외가 발생하지만 일반 예외 필터Application_Error 에서 메서드를 Global.asax.cs사용하여 예외 를 포착 할 수 있습니다 .

하지만 재미있는 점은 응용 프로그램을 충돌시킬 수있는 처리되지 않은 모든 예외를 항상 기록하기 때문에 이러한 예외를 포착하지 않는 것을 선호한다는 것입니다. 하지만 내가 틀릴 수 있습니다). 이러한 오류는 일부 시간 제한 만료 또는 클라이언트의 명시 적 취소로 인해 나타나는 것으로 생각되지만 ASP.NET 프레임 워크 내부에서 처리되고 처리되지 않은 예외로 전파되지 않을 것으로 예상했을 것입니다.


제 경우에는 사용자가 새 URL로 이동할 때 브라우저가 요청을 취소하기 때문에 이러한 예외가 발생합니다.
Bates Westmoreland 2014 년

1
내가 참조. 제 경우 요청은 WinHTTP브라우저가 아닌 API를 통해 발행됩니다 .
Gabriel S.

2

이 오류에 대해 좀 더 자세한 내용을 찾았습니다. 발생할 수있는 두 가지 예외가 있습니다.

  1. OperationCanceledException
  2. TaskCanceledException

첫 번째는 컨트롤러의 코드가 실행되는 동안 연결이 끊어지면 발생합니다 (또는 그 주변의 일부 시스템 코드). 두 번째는 실행이 속성 (예 :) 내에있는 동안 연결이 끊어지면 발생합니다 AuthorizeAttribute.

따라서 제공된 해결 방법 은 첫 번째 예외를 부분적으로 완화하는 데 도움이되며 두 번째 예외는 도움이되지 않습니다. 후자의 경우 취소 토큰이 true로 설정되는 대신 호출 자체 에서 TaskCanceledException발생 base.SendAsync합니다.

이 문제를 해결하는 두 가지 방법을 볼 수 있습니다.

  1. global.asax에서 두 예외를 모두 무시합니다. 그렇다면 갑자기 중요한 것을 대신 무시할 수 있는가?
  2. 핸들러에서 추가 try / catch 수행 (방탄은 아니지만 + TaskCanceledException우리가 무시할 가능성이 여전히 로그하려는 항목이 될 가능성 이 있습니다.

config.MessageHandlers.Add(new CancelledTaskBugWorkaroundMessageHandler());

class CancelledTaskBugWorkaroundMessageHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

            // Try to suppress response content when the cancellation token has fired; ASP.NET will log to the Application event log if there's content in this case.
            if (cancellationToken.IsCancellationRequested)
            {
                return new HttpResponseMessage(HttpStatusCode.InternalServerError);
            }
        }
        catch (TaskCancellationException)
        {
            // Ignore
        }

        return response;
    }
}

잘못된 예외를 정확히 찾아 낼 수있는 유일한 방법은 stacktrace에 Asp.Net 항목이 포함되어 있는지 확인하는 것입니다. 그래도 매우 견고하지 않습니다.

추신 다음은 이러한 오류를 필터링하는 방법입니다.

private static bool IsAspNetBugException(Exception exception)
{
    return
        (exception is TaskCanceledException || exception is OperationCanceledException) 
        &&
        exception.StackTrace.Contains("System.Web.HttpApplication.ExecuteStep");
}

1
제안 된 코드 response에서 try 내부에 변수 를 만들고 try 외부에 반환합니다. 그것은 불가능한 일이 될 수 있습니까? 또한 IsAspNetBugException을 어디에서 사용합니까?
Schoof

1
아니요, 작동하지 않습니다. 물론 try / catch 블록 외부에서 선언하고 완료된 작업과 같은 것으로 초기화해야합니다. 어쨌든 방탄이 아닌 솔루션의 예일뿐입니다. 다른 질문에 관해서는 Global.Asax OnError 핸들러에서 사용합니다. 메시지를 기록하는 데 사용하지 않아도 어쨌든 걱정할 필요가 없습니다. 이 경우 시스템에서 "오류 없음"을 필터링하는 방법의 예입니다.
Ilya Chernomordik

1

우리는 동일한 예외를 받고 있으며 @dmatson의 해결 방법을 사용하려고 시도했지만 여전히 예외가 발생했습니다. 우리는 최근까지 그것을 다루었습니다. 일부 Windows 로그가 놀라운 속도로 증가하는 것을 발견했습니다.

다음 위치에있는 오류 파일 : C : \ Windows \ System32 \ LogFiles \ HTTPERR

대부분의 오류는 모두 "Timer_ConnectionIdle"에 대한 것입니다. 주변을 검색 한 결과 웹 API 호출이 완료 되었음에도 불구하고 연결이 원래 연결을지나 2 분 동안 지속되는 것 같습니다.

그런 다음 응답에서 연결을 닫고 어떤 일이 발생하는지 확인해야한다고 생각했습니다.

response.Headers.ConnectionClose = true;SendAsync MessageHandler에 추가 했으며 클라이언트에게 연결이 닫히고 더 이상 문제가 발생하지 않는다는 것을 알 수 있습니다.

이것이 최선의 해결책은 아니지만 우리의 경우에는 작동합니다. 나는 또한 성능 측면에서 API가 동일한 클라이언트에서 연속적으로 여러 호출을받는 경우 수행하고 싶은 작업이 아니라고 확신합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.