multithreading asynchronous asp.net-web-api ninject httpcontext

multithreading - Usar HttpContext.Current en WebApi es peligroso debido a la asincronización



asynchronous asp.net-web-api (3)

HttpContext.Current obtiene el contexto actual por Thread (investigué la implementación directamente).

Sería más correcto decir que HttpContext se aplica a un hilo; o un hilo "ingresa" al HttpContext .

Uso de HttpContext.Current dentro de la tarea asíncrona no es posible, porque puede ejecutarse en otro subproceso.

De ningún modo; el comportamiento predeterminado de async / await se reanudará en un hilo arbitrario, pero ese hilo ingresará al contexto de solicitud antes de reanudar su método async .

La clave para esto es el SynchronizationContext . Tengo un artículo de MSDN sobre el tema si no estás familiarizado con él. Un SynchronizationContext define un "contexto" para una plataforma, siendo los comunes los contextos de UI (WPF, WinPhone, WinForms, etc.), el contexto del grupo de subprocesos y el contexto de solicitud de ASP.NET.

El contexto de solicitud ASP.NET gestiona HttpContext.Current así como algunas otras cosas como cultura y seguridad. Los contextos de la interfaz de usuario están estrechamente asociados con un único hilo ( el hilo de la interfaz de usuario), pero el contexto de la solicitud de ASP.NET no está vinculado a un hilo específico. Sin embargo, solo permitirá un hilo en el contexto de solicitud a la vez .

La otra parte de la solución es cómo async y await funcione. Tengo una introducción async en mi blog que describe su comportamiento . En resumen, await por defecto capturará el contexto actual (que es SynchronizationContext.Current menos que sea null ), y usará ese contexto para reanudar el método async . Por lo tanto, await captura automáticamente ASP.NET SynchronizationContext y reanudará el método async dentro de ese contexto de solicitud (preservando así culture, security y HttpContext.Current ).

Si await ConfigureAwait(false) , está diciendo explícitamente que no capturará el contexto.

Tenga en cuenta que ASP.NET tuvo que cambiar su SynchronizationContext para trabajar limpiamente con async / await . Debe asegurarse de que la aplicación está compilada contra .NET 4.5 y también se dirige explícitamente a 4.5 en su web.config ; este es el valor predeterminado para los nuevos proyectos de ASP.NET 4.5, pero debe establecerse explícitamente si actualizó un proyecto existente de ASP.NET 4.0 o anterior.

Puede asegurarse de que estas configuraciones sean correctas ejecutando su aplicación contra .NET 4.5 y observando SynchronizationContext.Current . Si es AspNetSynchronizationContext , entonces eres bueno; si es LegacyAspNetSynchronizationContext , entonces la configuración es incorrecta.

Siempre que la configuración sea correcta (y esté utilizando ASP.NET 4.5 AspNetSynchronizationContext ), puede usar HttpContext.Current forma segura después de await sin preocuparse por ello.

Mi pregunta está un poco relacionada con esto: equivalente de WebApi para HttpContext.Items con Dependency Injection .

Queremos inyectar una clase usando HttpContext.Current en el área de WebApi usando Ninject.

Mi preocupación es que esto podría ser muy peligroso , ya que en WebApi ( ¿todo? ) Es asincrónico.

Por favor corrígeme si me equivoco en estos puntos, esto es lo que investigué hasta ahora:

  1. HttpContext.Current obtiene el contexto actual por Thread (investigué la implementación directamente).

  2. Uso de HttpContext.Current dentro de la tarea asíncrona no es posible, porque puede ejecutarse en otro subproceso.

  3. WebApi usa IHttpController con el método Task<HttpResponseMessage> ExecuteAsync => cada solicitud es async => no puede usar HttpContext.Current dentro del método de acción. Incluso podría suceder, más solicitudes se ejecutan en el mismo hilo por coicidence.

  4. Para crear controladores con material inyectado en los constructores, IHttpControllerActivator se utiliza con el método de sincronización IHttpController Create . Esto es, donde ninject crea el Controlador con todas sus dependencias.

  • Si estoy en lo correcto en estos 4 puntos, el uso de HttpContext.Current dentro de un método de acción o cualquier otra capa es muy peligroso y puede tener resultados inesperados. Vi tantas respuestas aceptadas que sugerían exactamente esto. En mi humilde opinión esto puede funcionar por un tiempo, pero fallará bajo carga.

  • Pero al usar DI para crear un Controlador y sus dependencias, está Bien, porque esto se ejecuta en un subproceso separado. Podría obtener un valor del HttpContext en el constructor y sería seguro? . Me pregunto si cada controlador se crea en un solo hilo para cada solicitud, ya que esto podría causar problemas en cargas pesadas, donde todos los hilos de IIS podrían ser consumidos.

Solo para explicar por qué quiero inyectar cosas de HttpContext:

  • Una solución sería obtener la solicitud en el método de acción del controlador y pasar el valor necesario de todas las capas como parámetro hasta que se use en algún lugar profundo del código.
  • nuestra solución deseada: todas las capas intermedias no se ven afectadas por esto, y podemos usar la solicitud inyectada en algún lugar profundo del código (por ejemplo, en algún Proveedor de configuración que dependa de la URL)

    Por favor, dame tu opinión si estoy totalmente equivocado o si mis sugerencias son correctas, ya que este tema parece ser muy complicado. Thx por adelantado!


Encontré un artículo muy bueno que describe exactamente este problema: http://byterot.blogspot.cz/2012/04/aspnet-web-api-series-part-3-async-deep.html?m=1

El autor investigó profundamente cómo se llama al método ExecuteAsync en el marco de WebApi y llegó a esta conclusión:

Las acciones de ASP.NET Web API (y todos los métodos de interconexión) se llamarán de forma asíncrona solo si devuelve una Tarea o Tarea <T>. Esto puede parecer obvio, pero ninguno de los métodos de interconexión con sufijo Async se ejecutará en sus propios subprocesos. Usar manta Async podría ser un nombre inapropiado. [ACTUALIZACIÓN: el equipo de ASP.NET de hecho ha confirmado que Async se usa para designar métodos que devuelven la tarea y pueden ejecutarse de forma asíncrona, pero no es necesario]

Lo que entendí del artículo es que los métodos de acción se llaman sincrónicamente, pero es la decisión de la persona que llama.

Creé una pequeña aplicación de prueba para este propósito, algo como esto:

public class ValuesController : ApiController { public object Get(string clientId, string specialValue) { HttpRequest staticContext = HttpContext.Current.Request; string staticUrl = staticContext.Url.ToString(); HttpRequestMessage dynamicContext = Request; string dynamicUrl = dynamicContext.RequestUri.ToString(); return new {one = staticUrl, two = dynamicUrl}; } }

y una versión Async que devuelve la async Task<object>

Traté de hacer un pequeño ataque de DOS con jquery y no pude determinar ningún problema hasta que lo utilicé, await Task.Delay(1).ConfigureAwait(false); , lo cual es obvio, fracasará.

Lo que tomé del artículo es que el problema es muy complicado y el cambio de hilo puede ocurrir cuando se usa el método de acción asíncrono, por lo que definitivamente NO es una buena idea usar HttpContext.Current en cualquier parte del código llamado desde los métodos de acción. Pero como el controlador se crea de forma síncrona, está bien utilizar HttpContext.Current en el constructor y también en la inyección de dependencias.

Cuando alguien tiene otra explicación para este problema, corrígeme ya que este problema es muy complicado y todavía no estoy 100% convencido.

diclaimer:

  • Ignoro por ahora el problema de Web-Api autohospedado withoud IIS, donde HttpContext.Current no funcionaría probablemente de todos modos. Ahora dependemos de IIS.

Estoy usando una API web, que está utilizando la metodología async / await.

también usando

1) HttpContext.Current.Server.MapPath 2) System.Web.HttpContext.Current.Request.ServerVariables

Esto funcionó bien durante una buena cantidad de tiempo que se interrumpió repentinamente sin cambio de código.

Pasar mucho tiempo volviendo a versiones anteriores anteriores, encontró que la clave que falta causa el problema.

< httpRuntime targetFramework="4.5.2" /> under system.web

No soy un experto técnicamente. Pero sugiero agregar la clave a su configuración web y darle un IR .