it-source

브라우저가 요청을 취소하는 경우 ASP.NET Web API OperationCancelledException

criticalcode 2023. 5. 21. 11:38
반응형

브라우저가 요청을 취소하는 경우 ASP.NET Web API OperationCancelledException

사용자가 페이지를 로드할 때 하나 이상의 Ajax 요청을 수행하여 ASP.NET Web API 2 컨트롤러를 히트시킵니다.사용자가 다른 페이지로 이동하면 이러한 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)

스택 추적을 보면 예외가 여기서 던져지는 것을 알 수 있습니다. https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs#L413

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

사용자 코드를 벗어난 것 같습니다...

주의:

  • ASP.NET 웹 API 2를 사용하고 있습니다.
  • 웹 API 끝점은 비동기식 및 비비동기식 메서드가 혼합되어 있습니다.
  • 오류 기록을 추가하는 위치에 상관없이 사용자 코드에서 예외를 감지할 수 없는 경우
    • 전역as.asaxApplicaiton_Error
    • TaskScheduler.UnobservedTaskException
    • 필터링 ELMAH 파일void ErrorLog_Filtering(https://code.google.com/p/elmah/wiki/ErrorFiltering)

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

결국 문제는 이 경우 취소된 작업을 ASP.NET으로 반환하고 ASP.NET은 처리되지 않은 예외처럼 처리합니다(애플리케이션 이벤트 로그에 문제를 기록함).

그 동안 아래 코드와 같은 것을 시도해 볼 수 있습니다.취소 토큰이 실행될 때 내용을 제거하는 최상위 메시지 처리기를 추가합니다.응답에 내용이 없으면 버그가 트리거되지 않아야 합니다.메시지 처리기가 취소 토큰을 확인한 직후에 상위 수준의 웹 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;
    }
}

는 WebApi 예 로 구 다 을 좋 것 습 니 다 이 는 하 확 장 외 음 거 때 는 를 현 할 용 ▁the 니 다 ▁when ▁for 좋 ▁to 습 ▁web ▁recommend ▁is ▁extend ▁web ing ▁it▁logger▁an ▁implement ▁exception 예 것apiSystem.Web.Http.ExceptionHandling.ExceptionLogger클래스를 만듭니다.WebApi 내부에서는 취소된 요청에 대해 ExceptionLoggers의 Log 메서드를 호출하지 않습니다(그러나 예외 필터는 요청을 가져옵니다).이것은 의도적인 것입니다.

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

다음은 이 문제에 대한 다른 해결 방법입니다. 시작 에 OWIN 파이사용분OWIN 들미를OWIN 같이얻OWIN 점을을OWIN 수있다니습OWIN 만 하면 .OperationCanceledException:

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

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

  1. OperationCanceledException
  2. TaskCanceledException

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

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

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

  1. global.asax에서 두 예외를 모두 무시하고 있습니다.그렇다면 중요한 것을 갑자기 무시하는 것이 가능한지에 대한 질문이 나옵니다.
  2. 핸들러에서 추가 시도/캐치 수행(방탄성은 아니지만 + 여전히 가능성 있음)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;
    }
}

제가 알아낸 유일한 방법은 스택 추적에 일부 Asp가 포함되어 있는지 확인하는 것입니다.그물 같은 것들.하지만 그다지 튼튼해 보이지는 않습니다.

추신: 다음과 같이 오류를 필터링합니다.

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

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

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

럼그드세요▁a드▁have세.static (이(가) static할 수 웹에 있습니다.AppDomain.UnhandledException.

그러나 이 예외는 코드로 처리하기도 전에 ASP.NET Web API 런타임 내의 어딘가에서 실제로 처리되고 있는 것으로 보입니다.

이 경우, 당신은 그것을 첫 번째 기회 예외로 잡을 수 있어야 합니다.AppDomain.CurrentDomain.FirstChanceException방법은 이렇습니다.이것이 당신이 찾고 있는 것이 아닐 수도 있다는 것을 이해합니다.

나는 가끔 내 웹 API 2 애플리케이션에서 동일한 2개의 예외를 받지만, 나는 그것들을 잡을 수 있습니다.Application_Error.Global.asax.cs일반 예외 필터를 사용합니다.

하지만 재미있는 것은, 저는 이러한 예외를 잡는 것을 선호하지 않는다는 것입니다. 왜냐하면 저는 항상 애플리케이션을 손상시킬 수 있는 처리되지 않은 모든 예외를 기록하기 때문입니다(그러나 이 두 가지는 저와 무관하며 적어도 충돌하지 않거나 충돌하지 않아야 하지만 제가 틀릴 수도 있습니다).이러한 오류는 시간 초과 만료 또는 클라이언트의 명시적 취소로 인해 나타날 수 있지만 ASP.NET 프레임워크 내부에서 처리되고 처리되지 않은 예외로 외부로 전파되지 않을 것으로 예상했습니다.

OP는 무시하고 싶은 욕망을 언급했습니다.System.OperationCanceledExceptionELMAH 내에서 그리고 심지어 올바른 방향으로 연결을 제공합니다.ELMAH는 원래 게시물보다 훨씬 발전했고 OP가 요청하는 것을 정확하게 수행할 수 있는 풍부한 기능을 제공합니다.프로그래밍 방식 및 선언적(구성 기반) 접근 방식을 요약한 이 페이지(아직 완료 중)를 참조하십시오.

제가 개인적으로 가장 좋아하는 것은 웹.config에서 직접 만든 선언적 접근법입니다.위에 링크된 가이드에 따라 구성 기반 ELMAH 예외 필터링을 위해 Web.config를 설정하는 방법에 대해 배우십시오.System.OperationCanceledException당신은 그것을 사용할 것입니다.is-type다음과 같은 주장:

<configuration>
  ...
  <elmah>
  ...
    <errorFilter>
      <test>
        <or>
          ...
          <is-type binding="BaseException" type="System.OperationCanceledException" />
          ...
        </or>
      </test>
    </errorFilter>
  </elmah>
  ...
</configuration>

동일한 예외가 발생했습니다. @dmatson의 해결 방법을 사용하려고 했지만 여전히 예외가 발생했습니다.우리는 최근까지 그것을 처리했습니다.일부 윈도우즈 로그가 놀라운 속도로 증가하는 것을 확인했습니다.

C:\Windows\에 있는 오류 파일시스템32\로그 파일\HTTPERR

대부분의 오류는 "Timer_ConnectionIdle"에 대한 것입니다.검색해보니 웹 api 통화가 끝났음에도 불구하고 연결이 원래 연결 이후 2분 동안 지속된 것 같습니다.

그런 다음 응답에서 연결을 닫고 무슨 일이 일어나는지 봐야 한다고 생각했습니다.

추가했습니다.response.Headers.ConnectionClose = true;SendAsync MessageHandler에 문의하여 클라이언트가 연결을 종료하고 있으며 더 이상 문제가 발생하지 않습니다.

이것이 최선의 해결책이 아니라는 것을 알지만, 우리의 경우에는 효과가 있습니다.또한 API가 동일한 클라이언트에서 여러 호출을 연속적으로 수신하는 경우에는 성능 측면에서 이 작업을 수행할 필요가 없다고 확신합니다.

Sentry SDK의 .UseSentry()다음과 같이:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                    .UseSentry(options =>
                    {
                        options.AddExceptionFilterForType<OperationCanceledException>();
                    })
                    .UseContentRoot(Directory.GetCurrentDirectory())
                    .UseStartup<Startup>();
            });
}

문서 링크: https://sentry-docs-git-sentry-ruby-40.sentry.dev/platforms/dotnet/guides/aspnetcore/무시 예외

문서에 따르면:

SentrySdk.Init(o => o.AddExceptionFilterForType<OperationCanceledException>());

최신 버전의 ASP.NET WebApi에서도 작동해야 합니다.

언급URL : https://stackoverflow.com/questions/22157596/asp-net-web-api-operationcanceledexception-when-browser-cancels-the-request

반응형