c# - ihttpcontextaccessor - microsoft aspnetcore http httpcontext
Cómo obtener la instancia de Microsoft.AspNet.Http.HttpContext en Class Constructor utilizando DI (4)
¿Por qué pasaría el HttpContext en el constructor? ¿Por qué no acceder directamente a él donde quieras?
public MyAppContext(IDataService dataService)
{
HttpContext mycontext = HttpContext.Current;
//do stuff here with mycontext
}
Estoy creando una aplicación desechable en MVC 6 y experimentando con diferentes arquitecturas para dependencias.
El problema al que me enfrento es cómo crear un objeto '' MyAppContext
'' personalizado específico para la aplicación. Esto requeriría cierta información de HttpContext
y cierta información de la base de datos, y será un repositorio de ámbito de solicitud para atributos específicos de la aplicación. Quiero pasar la instancia de HttpContext
al constructor de '' MyAppContext
''.
He creado con éxito un objeto '' DataService
'' con una interfaz IDataService
usando DI y esto funciona bien. La diferencia con la clase ''MyAppContext'' es que tiene dos parámetros en el constructor: el '' DataService
'' y el Microsoft.AspNet.Http.HttpContext
. Aquí está la clase MyAppContext:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
En el código de inicio, registro la instancia de DataService y la instancia de MyAppContext:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
Estoy esperando que el parámetro HttpContext
en el constructor se resuelva por DI. Cuando ejecuto el código, esta es la excepción que recibí:
InvalidOperationException: no se puede resolver el servicio para el tipo ''Microsoft.AspNet.Http.HttpContext'' al intentar activar ''MyAppContext''
Me imagino que esto se debe a que no hay una instancia específica de HttpContext
que se esté produciendo este error, pero no sé cómo registrar la instancia de HttpContext
en DI. app.UseRequestServices();
la línea '' app.UseRequestServices();
''Pero esto no ha hecho ninguna diferencia. También probé una variante de:
services.AddScoped<HttpContext, HttpContext>();
Pero esto falla porque se supone que el segundo HttpContext
es una instancia. Sé que no es correcto, pero no he podido averiguar qué es.
Entonces, en resumen, ¿cómo puedo pasar el objeto HttpContext
al constructor de MyAppContext?
Al inyectar un HttpContext
en su componente, está violando los principios de SOLID . Para ser más específico, estás violando:
- El Principio de Inversión de Dependencia (DIP) porque usted depende de un tipo de marco (el
HttpContext
). - El Principio de Segregación de Interfaz (ISP) porque el
HttpContext
tiene muchos métodos, mientras que el consumidor nunca los usa todos.
Ambas violaciones hacen que sea mucho más difícil probar su código. Aunque en su lugar puede inyectar el IHttpContextAccessor
como sugiere @victor, esto sigue siendo una violación tanto del DIP como del ISP, porque es una abstracción que proporciona el marco y aún depende de HttpContext. Según el DIP, es el cliente quien debe definir la abstracción. Esto hace que su código sea acoplado innecesariamente al marco.
En su lugar, debe esforzarse por especificar interfaces de rol estrechas; interfaces que hacen una cosa específica para usted que es específica a las necesidades de su aplicación. Inyectar un diccionario grande con valores de cadena (como lo que HttpContext
es, nunca es muy específico). De su pregunta no está claro qué tipo de datos necesita de MyAppContext
, pero espero algo como información del usuario que ha iniciado sesión actualmente. Para esto puedes definir una abstracción específica de IUserContext
, por ejemplo:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
Se puede crear fácilmente un adaptador que conecta la aplicación con el marco ASP.NET para esta abstracción:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
Este adaptador depende de IHttpContextAccessor
, pero esto está bien, ya que el adaptador es un componente de infraestructura ubicado en la raíz de composición . Hay varias maneras de registrar esta clase, por ejemplo:
services.AddSingleton<IUserContext, AspNetUserContext>();
En la clase de inicio:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvcCore();
}
En el controlador:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
private readonly IHttpContextAccessor _httpContextAccessor;
public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Inyectar IHttpContextAccessor
en el constructor