tmm360
tmm360

Reputation: 414

Asp.Net Core - Need to access to HttpContext instance from instances generated without Dependency Injection

I'm using Asp.Net Core RC1, and I've to access to an HttpContext instance from instances generated by a model generator (from interceptors of Castle.Core, for be exact). Model generator has to be a single instance through the entire application.

I need to create an instance of ModelGenerator into startup file, because it is used into static lambdas needed to configure some serializers. Serializers are statically registered, so I have to write into startup:

var modelGenerator = new ModelGenerator();
Serializers.Configure(modelGenerator); // static use of model generator instance

I also add modelGenerator as singleton instance for other uses with DI.

services.AddInstance<IModelGenerator>(modelGenerator);

What I would have done with DI is to take a IHttpContextAccessor interface from ModelGenerator's constructor, but into this context I can't because I don't have an instance on startup. I need something like a ServiceLocator to call from ModelGenerator, or some other patter that I ignore.

How can reach an updated HttpContext instance, with information of current request, from interceptors generated by ModelGenerator?

Upvotes: 2

Views: 4053

Answers (1)

NightOwl888
NightOwl888

Reputation: 56849

It appears that there is no way to get an instance of HttpContext in application startup. This makes sense - in previous versions of MVC this wasn't possible in IIS integrated mode or OWIN.

So what you have are 2 issues:

  • How do you get the IHttpContextAccessor into your serializer?
  • How do you ensure the HttpContext is not accessed until it is available?

The first issue is pretty straightforward. You just need to use constructor injection on IHttpContextAccessor.

public interface ISerializer
{
    void Test();
}

public class ModelGenerator : ISerializer
{
    private readonly IHttpContextAccessor httpContextAccessor;

    public ModelGenerator(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Test()
    {
        var context = this.httpContextAccessor.HttpContext;

        // Use the context
    }
}

And to register...

// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
    // Other code...

    // Add the model generator
    services.AddTransient<ISerializer, ModelGenerator>();
}

// 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)
{
    var serializers = app.ApplicationServices.GetServices<ISerializer>();
    foreach (var serializer in serializers)
    {
        Serializers.Configure(serializer);
    }

    // Other code...
}

The second issue can be resolved by moving whatever initialization calls that you require HttpContext in into a global filter.

public class SerializerFilter : IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext context)
    {
        // TODO: Put some kind of if condition (possibly a 
        // global static variable) here to ensure this 
        // only runs when needed.
        Serializers.Test();
    }
}

And to register the filter globally:

public void ConfigureServices(IServiceCollection services)
{
    // Other code...

    // Add the global filter for the serializer
    services.AddMvc(options =>
    {
        options.Filters.Add(new SerializerFilter());
    });

    // Other code...
}

If your Serializers.Configure() method requires HttpContext to work, then you will need to move that call into the global filter.

Upvotes: 3

Related Questions