sebu
sebu

Reputation: 2954

How to get HTTP header value from startup.cs in asp.net core?

I need to create DbContext for each lifetime scope and the database name is not fixed, for each lifetime scope I want to have the DB from a different local store, to get the local store's value I need a key and I want to get that key from HTTPContext or IHttpContextAccessor. So, how can I get the value of the HTTP header in startup.cs?

N.B: I don't have any plan to send DB name through the header, this code is just an example, I'll update my code using key-value and some store service for the database name. The given code example always execute for each request if the request reference to the database.

services.AddDbContext<CheesedDbContext>((options =>
{
    options.UseSqlServer($"Server=.;Database={DB};Trusted_Connection=True;MultipleActiveResultSets=true;");
            
});

Upvotes: 1

Views: 5947

Answers (2)

iSR5
iSR5

Reputation: 3498

this is the first time I see someone who stores the database name in the request headers!. You should consider refactoring your way of storing that, as you don't want to expose any part of your connectionString to the public.

as an alternative, you can create an entry database that stores the database names, and users, each user would mapped to one database and give it a unique security stamp. Use that security stamp to get the stored database name. and reinitiate the context with a new connectionString.

for the IHttpContextAccessor part. You can register IHttpContextAccessor in the startup like this :

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    //register IHttpContextAccessor
    services.AddHttpContextAccessor();
}

Now you can use it in your DbContext :

public class CheesedDbContext : DbContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;
        
    public CheesedDbContext(IHttpContextAccessor httpContextAccessor)
    {
       _httpContextAccessor = httpContextAccessor;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var dbName = _context.HttpContext.Request.Headers["Db"];
        
        if(string.IsNullOrWhiteSpace(dbName))
        {
            throw new ArgumentNullException(nameof(dbName));
        }
        
        optionsBuilder.UseSqlServer($"Server=.;Database={dbName};Trusted_Connection=True;MultipleActiveResultSets=true;");
    }
}

You can also use ActionFilter to work with the IHttpContextAccessor on each request. Like this :

public class SomeFilterAttribute : ActionFilterAttribute
{

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        //Get header 
        var requestHeaders = context.HttpContext.Request.Headers["somekey"];
        
        // do some sync actions ..etc. 
        base.OnActionExecuted(context);
    }

    public async override Task OnActionExecutionAsync(ActionExecutingContext context , ActionExecutionDelegate next)
    {            

        //Get header 
        var requestHeaders = context.HttpContext.Request.Headers["somekey"];
        
        // do some async actions ..etc. 
        await base.OnActionExecutionAsync(context, next);
    }
}

then register it in the Startup :

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.Filters.Add(new SomeFilterAttribute());
    });
}

this can be applied on both AddControllers and AddControllersWithViews.

Upvotes: 2

sebu
sebu

Reputation: 2954

I solved it using services.BuildServiceProvider()!

services.AddDbContext<CheesedDbContext>(options =>
{
    var serviceProvider = services.BuildServiceProvider();
    var db = serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext?.Request?.Headers["Db"];
    options.UseSqlServer($"Server=.;Database={db.Value};Trusted_Connection=True;MultipleActiveResultSets=true;");
});

Upvotes: 1

Related Questions