web-dev-qa-db-fra.com

Comment obtenir HttpContext.Current dans ASP.NET Core?

Nous sommes en train de réécrire/convertir notre application ASP.NET WebForms à l'aide d'ASP.NET Core. Essayer d'éviter la réingénierie autant que possible.

Il y a une section où nous utilisons HttpContext dans une bibliothèque de classes pour vérifier l'état actuel. Comment accéder à HttpContext.Current dans .NET Core 1.0?

 var current = HttpContext.Current;
     if (current == null)
      {
       // do something here
       // string connection = Configuration.GetConnectionString("MyDb");
      }

Je dois accéder à cela afin de construire l'application hôte actuelle.

$"{current.Request.Url.Scheme}://{current.Request.Url.Host}{(current.Request.Url.Port == 80 ? "" : ":" + current.Request.Url.Port)}";
165
HaBo

En règle générale, la conversion d'une application Web Forms ou MVC5 en ASP.NET Core nécessite une quantité importante de refactoring.

HttpContext.Current a été supprimé de ASP.NET Core. L'accès au contexte HTTP actuel à partir d'une bibliothèque de classes distincte est le type d'architecture désordonnée qu'ASP.NET Core tente d'éviter. Il existe plusieurs façons de réorganiser cela dans ASP.NET Core.

Propriété HttpContext

Vous pouvez accéder au contexte HTTP actuel via la propriété HttpContext de tout contrôleur. La chose la plus proche de votre exemple de code original serait de passer HttpContext dans la méthode que vous appelez:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        MyMethod(HttpContext);

        // Other code
    }
}

public void MyMethod(Microsoft.AspNetCore.Http.HttpContext context)
{
    var Host = $"{context.Request.Scheme}://{context.Request.Host}";

    // Other code
}

Paramètre HttpContext dans le middleware

Si vous écrivez middleware personnalisé pour le pipeline ASP.NET Core, le HttpContext de la requête en cours est automatiquement transmis à votre méthode Invoke:

public Task Invoke(HttpContext context)
{
    // Do something with the current HTTP context...
}

Accesseur de contexte HTTP

Enfin, vous pouvez utiliser le service d'assistance IHttpContextAccessor pour obtenir le contexte HTTP de toute classe gérée par le système d'injection de dépendances ASP.NET Core. Ceci est utile lorsque vous avez un service commun utilisé par vos contrôleurs.

Demander cette interface dans votre constructeur:

public MyMiddleware(IHttpContextAccessor httpContextAccessor)
{
    _httpContextAccessor = httpContextAccessor;
}

Vous pouvez ensuite accéder au contexte HTTP actuel de manière sécurisée:

var context = _httpContextAccessor.HttpContext;
// Do something with the current HTTP context...

IHttpContextAccessor n'est pas toujours ajouté au conteneur de services par défaut. Enregistrez-le donc dans ConfigureServices juste pour être sûr:

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpContextAccessor();
    // if < .NET Core 2.2 use this
    //services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Other code...
}
210
Nate Barbettini

Nécromancie.
OUI VOUS POUVEZ, et voici comment.
Un secret pour les grands migrants jonques morceaux de code:
La méthode suivante est un mauvais plan d’un hack qui est activement engagé dans le travail express de satan (aux yeux des développeurs du framework .NET Core), , mais il travaux :

Dans public class Startup

ajouter une propriété

public IConfigurationRoot Configuration { get; }

Et puis ajoutez un singleton IHttpContextAccessor à DI dans ConfigureServices.

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

Puis dans Configure

    public void Configure(
              IApplicationBuilder app
             ,IHostingEnvironment env
             ,ILoggerFactory loggerFactory
    )
    {

ajoutez le paramètre DI IServiceProvider svp, de sorte que la méthode se présente comme suit:

    public void Configure(
           IApplicationBuilder app
          ,IHostingEnvironment env
          ,ILoggerFactory loggerFactory
          ,IServiceProvider svp)
    {

Ensuite, créez une classe de remplacement pour System.Web:

namespace System.Web
{

    namespace Hosting
    {
        public static class HostingEnvironment 
        {
            public static bool m_IsHosted;

            static HostingEnvironment()
            {
                m_IsHosted = false;
            }

            public static bool IsHosted
            {
                get
                {
                    return m_IsHosted;
                }
            }
        }
    }


    public static class HttpContext
    {
        public static IServiceProvider ServiceProvider;

        static HttpContext()
        { }


        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                // var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
                object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor));

                // Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
                Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext;
                // context.Response.WriteAsync("Test");

                return context;
            }
        }


    } // End Class HttpContext 


}

Maintenant, dans Configure, où vous avez ajouté le IServiceProvider svp, enregistrez ce fournisseur de services dans la variable statique "ServiceProvider" de la classe factice System.Web.HttpContext créée (System.Web.HttpContext.ServiceProvider).

et définissez HostingEnvironment.IsHosted sur true

System.Web.Hosting.HostingEnvironment.m_IsHosted = true;

c'est essentiellement ce que System.Web a fait, mais vous ne l'avez jamais vu (je suppose que la variable a été déclarée comme étant interne plutôt que publique).

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    ServiceProvider = svp;
    System.Web.HttpContext.ServiceProvider = svp;
    System.Web.Hosting.HostingEnvironment.m_IsHosted = true;


    app.UseCookieAuthentication(new CookieAuthenticationOptions()
    {
        AuthenticationScheme = "MyCookieMiddlewareInstance",
        LoginPath = new Microsoft.AspNetCore.Http.PathString("/Account/Unauthorized/"),
        AccessDeniedPath = new Microsoft.AspNetCore.Http.PathString("/Account/Forbidden/"),
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        CookieSecure = Microsoft.AspNetCore.Http.CookieSecurePolicy.SameAsRequest

       , CookieHttpOnly=false

    });

Comme dans les formulaires Web ASP.NET, vous obtenez un NullReference lorsque vous essayez d'accéder à un HttpContext lorsqu'il n'y en a aucun, tel qu'il était auparavant dans Application_Start dans global.asax.

Je souligne encore, cela ne fonctionne que si vous avez réellement ajouté

services.AddSingleton<Microsoft.AspNetCore.Http.IHttpContextAccessor, Microsoft.AspNetCore.Http.HttpContextAccessor>();

comme je l'ai écrit, vous devriez.
Bienvenue dans le modèle ServiceLocator dans le modèle DI;)
Pour les risques et les effets secondaires, demandez à votre médecin ou à votre pharmacien - ou étudiez les sources de .NET Core à github.com/aspnet , et effectuez des tests.


Peut-être une méthode plus maintenable serait-elle d’ajouter cette classe d’aide

namespace System.Web
{

    public static class HttpContext
    {
        private static Microsoft.AspNetCore.Http.IHttpContextAccessor m_httpContextAccessor;


        public static void Configure(Microsoft.AspNetCore.Http.IHttpContextAccessor httpContextAccessor)
        {
            m_httpContextAccessor = httpContextAccessor;
        }


        public static Microsoft.AspNetCore.Http.HttpContext Current
        {
            get
            {
                return m_httpContextAccessor.HttpContext;
            }
        }


    }


}

Et puis en appelant HttpContext.Configure dans Startup-> Configure

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider svp)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();


    System.Web.HttpContext.Configure(app.ApplicationServices.
        GetRequiredService<Microsoft.AspNetCore.Http.IHttpContextAccessor>()
    );
41
Stefan Steiger

Il existe une solution à cela si vous avez vraiment besoin d'un accès statique au contexte actuel. Dans Startup.Configure (….)

app.Use(async (httpContext, next) =>
{
    CallContext.LogicalSetData("CurrentContextKey", httpContext);
    try
    {
        await next();
    }
    finally
    {
        CallContext.FreeNamedDataSlot("CurrentContextKey");
    }
});

Et quand vous en avez besoin, vous pouvez l'obtenir avec:

HttpContext context = CallContext.LogicalGetData("CurrentContextKey") as HttpContext;

J'espère que ça aide. N'oubliez pas que cette solution de contournement se produit lorsque vous n'avez pas le choix. La meilleure pratique consiste à utiliser l'injection de dépendance.

9
HLS