iis async - ASP.NET Web API OperationCanceledException cuando el navegador cancela la solicitud




await task (6)

Puede intentar cambiar el comportamiento de manejo de excepciones de tarea TPL predeterminado a través de web.config :

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

Luego, tenga una clase static (con un constructor static ) en su aplicación web, que manejaría AppDomain.UnhandledException .

Sin embargo, parece que esta excepción en realidad se está manejando en algún lugar dentro del tiempo de ejecución de ASP.NET Web API , incluso antes de que tenga la oportunidad de manejarlo con su código.

En este caso, debería ser capaz de detectarlo como una excepción de la primera oportunidad, con AppDomain.CurrentDomain.FirstChanceException , así es cómo . Entiendo que esto puede no ser lo que estás buscando.

Cuando un usuario carga una página, realiza una o más solicitudes ajax, que afectan a los controladores ASP.NET Web API 2. Si el usuario navega a otra página, antes de completar estas solicitudes ajax, el navegador cancela las solicitudes. Nuestro ELMAH HttpModule registra dos errores por cada solicitud cancelada:

Error 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()

Error 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)

Al observar el stacktrace, veo que la excepción se está lanzando desde aquí: https://github.com/ASP-NET-MVC/aspnetwebstack/blob/master/src/System.Web.Http.WebHost/HttpControllerHandler.cs#L413

Mi pregunta es: ¿cómo puedo manejar e ignorar estas excepciones?

Parece estar fuera del código de usuario ...

Notas:

  • Estoy usando ASP.NET Web API 2
  • Los puntos finales de la API web son una mezcla de métodos asincrónicos y no asincrónicos.
  • No importa dónde agregue el registro de errores, no puedo detectar la excepción en el código de usuario

Hemos estado recibiendo la misma excepción, hemos intentado utilizar la solución de @ dmatson, pero todavía obtendríamos alguna excepción. Lo tratamos hasta hace poco. Notamos que algunos registros de Windows crecían a un ritmo alarmante.

Archivos de error ubicados en: C: \ Windows \ System32 \ LogFiles \ HTTPERR

La mayoría de los errores fueron todos para "Timer_ConnectionIdle". Busqué alrededor y parecía que a pesar de que la llamada a la API web había finalizado, la conexión aún persistía durante dos minutos después de la conexión original.

Luego pensé que deberíamos tratar de cerrar la conexión en la respuesta y ver qué pasa.

Agregué response.Headers.ConnectionClose = true; al SendAsync MessageHandler y por lo que puedo decir, los clientes están cerrando las conexiones y ya no estamos experimentando el problema.

Sé que esta no es la mejor solución, pero funciona en nuestro caso. También estoy bastante seguro de que, en lo que respecta al rendimiento, esto no es algo que quieras hacer si tu API recibe múltiples llamadas del mismo cliente una detrás de la otra.


Este es un error en ASP.NET Web API 2 y, lamentablemente, no creo que haya una solución que siempre tenga éxito. Archivamos un bug para arreglarlo de nuestro lado.

En última instancia, el problema es que devolvemos una tarea cancelada a ASP.NET en este caso, y ASP.NET trata una tarea cancelada como una excepción no controlada (registra el problema en el registro de eventos de la aplicación).

Mientras tanto, puedes probar algo como el siguiente código. Agrega un controlador de mensajes de nivel superior que elimina el contenido cuando se activa el token de cancelación. Si la respuesta no tiene contenido, el error no debería activarse. Todavía hay una pequeña posibilidad de que ocurra, porque el cliente podría desconectarse justo después de que el manejador de mensajes verifique el token de cancelación pero antes de que el código API de nivel superior haga el mismo chequeo. Pero creo que ayudará en la mayoría de los casos.

David

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;
    }
}

A veces obtengo las mismas 2 excepciones en mi aplicación Web API 2, sin embargo, puedo verlas con el método Application_Error en Global.asax.cs y usar un filtro de excepción genérico.

Lo curioso es, sin embargo, prefiero no detectar estas excepciones, porque siempre registro todas las excepciones no controladas que pueden bloquear la aplicación (estas 2, sin embargo, son irrelevantes para mí y aparentemente no o al menos no deberían bloquearse). eso, pero puedo estar equivocado). Sospecho que estos errores aparecen debido a la caducidad del tiempo de espera o la cancelación explícita del cliente, pero esperaba que se trataran dentro del marco ASP.NET y no se propagaran fuera de él como excepciones no controladas.


Al implementar un registrador de excepciones para WebApi, se recomienda extender la clase System.Web.Http.ExceptionHandling.ExceptionLogger lugar de crear un ExceptionFilter. Los internos de WebApi no llamarán al método de registro de ExceptionLoggers para las solicitudes canceladas (sin embargo, los filtros de excepción las obtendrán). Esto es por diseño.

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

Supongo que ha configurado su servidor correctamente para WebDeploy 2.0 según este artículo:

Configurar Web Deploy (IIS.NET)

Nota: MS ha lanzado una actualización de Web Deploy 2.0 y el enlace original ya no es válido. He actualizado esto, pero creo que será un objetivo móvil en el tiempo.

También necesita instalar Web Deploy 2.0 en su máquina de desarrollo / construcción / CI.

Si todavía usa 1.0, le recomiendo que actualice, hay algunas mejoras enormes en 2.0.

Uso de la característica de publicación de Visual Studio 2010:

Visual Studio puede publicar un sitio haciendo clic derecho en el sitio y seleccionando "Publicar". Esto abre el siguiente diálogo:

Hay un par de gotcha con Visual Studio 2010 y WebDeploy 2.0. El primero es que VS2010 no es consciente de WebDeploy / MSDeploy 2.0. Por lo tanto, si intenta publicar, obtendrá un error como el siguiente:

Error 1 Error en la tarea de despliegue web. ((02/04/2011 12:30:40) Se produjo un error cuando la solicitud se procesó en la computadora remota.)

También verá el siguiente error en el seguimiento de solicitudes fallidas para el servicio de administración web en el servidor en C:\inetpub\logs\wmsvc\TracingLogFiles\W3SVC1 suponiendo que tiene esto activado:

AspNetModuleDiagErrorEvent
Uri /msdeploy.axd
EventData Tracing deployment agent exception. Solicitud de ID ''. Fecha y hora de solicitud: '02 / 04/2011
System.UnauthorizedAccessException: acceso denegado a la ruta 'D: \'.

La letra de la unidad variará según la unidad en la que se encuentra su sitio IIS.

Fuera de la caja, el mecanismo de publicación en la GUI usa de manera predeterminada la versión incorrecta de MSDeploy (1.0). Queremos decirle a VS2010 que use MSDeploy 2.0. Puede hacerlo editando el archivo devenv.exe.config Visual Studio 2010 que se encuentra en (suponiendo que haya realizado una instalación predeterminada de c:\ drive):

Para sistemas de 64 bits: c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE Para sistemas de 32 bits: c:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE

Abre devenv.exe.config en tu editor XML favorito (yo solo uso Visual Studio 2010) y copia el siguiente xml:

<dependentAssembly>
  <assemblyIdentity 
    name="Microsoft.Web.Deployment" 
    publicKeyToken="31bf3856ad364e35" culture="neutral"/>
  <bindingRedirect oldVersion="7.1.0.0" newVersion="8.0.0.0"/>
</dependentAssembly>

Agregue esto a la sección /configuration/runtime/assemblyBinding :

Una vez que haya hecho esto, cierre todas las instancias de Visual Studio 2010 para permitir que este cambio surta efecto. Reinicie VS2010, abra un proyecto web y luego intente publicar de nuevo. Esta vez debería ser exitoso.

Publicación usando un paquete de compilación:

Visual Studio puede producir un paquete de compilación que se puede ejecutar desde la línea de comandos. Esto se genera utilizando Project -> Build Deployment Package . Útil para la integración continua y similares (el paquete también se puede generar usando msbuild con el modificador /t:Package ).

La carpeta de salida para el paquete generalmente está predeterminada en obj\Package .

Lamentablemente, Visual Studio 2010 se equivoca un poco y genera una secuencia de comandos por lotes de envoltura msdeploy dirigida a 1.0 y apuntar a la implementación en el servidor en lugar de al nivel del sitio.

No hay una solución rápida para esto aparte de crear su propia línea de comando msdeploy.exe. He dividido esto en varias líneas para hacer esto un poco más legible .:

"C:\Program Files\IIS\Microsoft Web Deploy v2\\msdeploy.exe"
  -source:archiveDir='d:\sites\DemoApp\obj\Package\Archive' 
  -dest:
       auto,
       computerName='https://yoursite.com:8172/msdeploy.axd?site=yoursitename',
       userName='demosite',
       password='somepassword',
       authtype='basic',
       includeAcls='False' 
  -verb:sync 
  -disableLink:AppPoolExtension 
  -disableLink:ContentExtension 
  -disableLink:CertificateExtension 
  -setParamFile:"d:\sites\DemoApp\obj\Package\Archive.SetParameters.xml"   
  -allowuntrusted

Lo primero a tener en cuenta es la ruta a msdeploy.exe . Visual Studio genera una ruta a la versión 1.0. Cambié esto para usar 2.0.

Parámetros notables:

-source:archiveDir= le dice a msdeploy que estamos implementando un paquete y brindamos la ubicación local

computerName='https://yoursite.com:8172/msdeploy.axd?site=yoursitename' - esto le dice a MSDEPLOY que se despliegue en un sitio específico en IIS7. yoursitename debe coincidir exactamente con el nombre del sitio en IIS.

nombre de usuario y password son el nombre del usuario administrador delegado para el sitio. Esto se configura mediante la función "Permisos del Administrador IIS" en el nivel del sitio. La cuenta debe ser una cuenta de usuario de Windows local.

-authtype='basic' - esto fuerza la autenticación básica, de lo contrario se intenta la autenticación NTLM.

- no se puede -allowuntrusted - esto ignora cualquier error de certificado SSL si usa el certificado SSL autofirmado incorporado.

Si usa esa línea de comando, podrá implementarla en un servidor remoto IIS7 con éxito.

Publicación de contenido sin procesar:

A veces queremos simplemente publicar contenido estático (o incluso un sitio ASP clásico o PHP) directamente desde una carpeta local. Podemos hacer esto usando la siguiente línea de comando msdeploy.exe :

"C:\Program Files\IIS\Microsoft Web Deploy v2\\msdeploy.exe" 
  -source:contentPath='d:\websites\mysite' 
  -dest:
     contentPath='yoursitename',
     computerName='https://yoursite.com:8172/msdeploy.axd?site=yoursitename',
     userName='demosite',
     password='somepassword',
     authtype='basic',
     includeAcls='False' 
-verb:sync 
-allowuntrusted 

De nuevo, se aplican las mismas reglas que antes para -dest:contentPath y computerName .

Creo que los problemas de la versión de MSDeploy se resolverán en SP1 (que aún no he podido ver).

Una final VS2010 Gotcha:

Al publicar con Visual Studio 2010, el paquete de compilación "Publicar" hace que las ACL de la cuenta anónima del sitio cambien a Solo lectura para todos los archivos y carpetas, con la excepción de la carpeta App_Data que se cambia a Lectura y Escritura.

Esto se puede .csproj agregando la siguiente configuración al archivo .csproj debajo de cada <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> :

<IncludeSetAclProviderOnDestination>False</IncludeSetAclProviderOnDestination>

O si estás usando msbuild:

msbuild.exe myproject.csproj /t:Package /p:IncludeSetAclProviderOnDestination=False

Encontré esa pepita útil de aquí:

Omitir la configuración de una ACL en un paquete de implementación de Visual Studio 2010 (enlace WayBackMachine porque el contenido original ya no está disponible)





asp.net iis asp.net-web-api task-parallel-library async-await