Getting the Base URL in ASP.Net Core - c#

I've just recently switched over from ASP.NET MVC to using .Core 2 and I can't figure out how to get the current URL in Core. I could get it easily enough using the Request in previous asp.net versions, but since that's no long valid in .Net Core I'm at a loss.
I haven't been able to find any way from my google searching as of now.
Any help is appreciated. Thanks!

In the ConfigureServices method of your Startup.cs file, add the line:
services.AddHttpContextAccessor();
and you will now have access to the IHttpContextAccessor interface throughout your code when using dependency injection.
Usage as follows:
public class CustomerRepository
{
private readonly IHttpContextAccessor _context;
public CustomerRepository(IHttpContextAccessor context)
{
_context = context;
}
public string BaseUrl()
{
var request = _context.HttpContext.Request;
// Now that you have the request you can select what you need from it.
return string.Empty;
}
}
Hope this answers your question :)

Try:
public class Startup {
public Startup(IConfiguration configuration, IHostingEnvironment env) {
Configuration = configuration;
HostingEnvironment = env;
var url = configuration[WebHostDefaults.ServerUrlsKey];
}
Please note that you can get more than one url.

Related

Need a way to get the current request URL to configure the database context in multi-tenant application

I am migrating a web app from asp.net mvc to .net core (.net 5), and this has got me stuck.
The site is configured in IIS to accept request from multiple URLs like site1.example.com and site2.example.com. Each site has its own database, accessed through entity framework core.
In the old .net framework, I was able to use one of the events in the global.asax.cs to parse the incoming request URL and lookup the correct tenant database from a configuration file. I'm trying to set up something similar in asp.net core mvc.
Here's the relevant part of my ConfigureServices method in the startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
services.AddDbContext<myDbContext>((serviceProvider, dbContextBuilder) =>
{
var tenantIdentifier = serviceProvider.GetRequiredService<ITenantIdentifier>();
var connectionString = Configuration.GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
dbContextBuilder.UseSqlServer(connectionString);
}, ServiceLifetime.Scoped);
//other services configured below...
}
Then the tenant identifier looks like this:
public interface ITenantIdentifier
{
string GetCurrentTenantId();
}
public class UrlTenantIdentifier : ITenantIdentifier
{
readonly IHttpContextAccessor _httpContextAccessor;
readonly ILogger<UrlTenantIdentifier> _logger;
public UrlTenantIdentifier(IHttpContextAccessor httpContextAccessor, ILogger<UrlTenantIdentifier> logger)
{
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
public string GetCurrentTenantId()
{
//_httpContextAccessor is null here
//logic below for parsing URL and finding if we're site1 or site2
}
}
Is there a correct way of doing this now that I'm not aware of? How can I set up the entity framework database context for dependency injection when I don't know the connection string key until runtime? Am I going to be stuck configuring separate sites and virtual directories in IIS?
Refactor the DbContext to override the OnConfiguring member. Inject configuration and context accessor and perform configuration there.
public class myDbContext : DbContext {
private readonly ITenantIdentifier tenantIdentifier;
private readonly IConfiguration configuration;
public myDbContext(IConfiguration configuration, ITenantIdentifier tenantIdentifier) {
this.configuration = configuration;
this.tenantIdentifier = tenantIdentifier;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
var connectionString = configuration
.GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
optionsBuilder.UseSqlServer(connectionString);
}
}
Trying to access the request context at the time the DbContext is being created/initialized is too early in the request flow to get access to the desired information. It needs to happen after the context has already been initialized and injected.
public void ConfigureServices(IServiceCollection services)
services.AddHttpContextAccessor();
services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
services.AddDbContext<myDbContext>(); //Simplified since configuration is internal
//other services configured below...
}
Reference DbContext Lifetime, Configuration, and Initialization

How to call a custom service at startup with dependencies in dotnet core 3?

I created a dotnet core MVC app. When the app starts the first time, I want to load some data from the database to a in-memory cache.
I know how to use the IMemoryCache, to fill data inside and to get them.
However, when the app start, I want to fill the in-memory cache with the data from the database. So I created a Singleton called ReferenceCache and the Interface IReferenceCache.
public interface IReferenceCache
{
public void Setup(IMemoryCache cache);
}
public class ReferenceCache : IReferenceCache
{
private ILogger<ReferenceCache> _logger;
private IMemoryCache _cache;
public void Setup(IMemoryCache cache)
{
//_logger = logger;
_cache = cache;
using (var context = new UtpmvContext())
{
var references = context.Reference.ToList();
_cache.Set("reference", references);
}
}
public List<Reference> GetSomeData()
{
var lsReferences = _cache.Get<List<Reference>>("reference");
List<Reference> liste = lsReferences.FindAll(x => x.ReferenceId == "ANY_ID");
return liste;
}
}
In the ConfigureServices I added my singleton:
services.AddSingleton<IReferenceCache, ReferenceCache>();
Then in the Configure section of the startup file, I added IReferenceCache and IMemoryCache in the signature and call my setup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IReferenceCache cache, IMemoryCache memoryCache)
{
// ... code removed for clarity
// I call my service to load the data in the in-memory
cache.Setup(memoryCache);
}
The thing is in my ReferenceCache class, I want to be able to log... so how can I add a dependency of ILogger?
I have never created any service in dotnet core before so please let me know if my design is not correct.
Thank you for your help! :)
You could resolve the ILogger<T> using the IServiceProvider that gets passed to the AddSingleton method:
services.AddSingleton<IReferenceCache>(serviceProvider =>
new ReferenceCache(serviceProvider.GetRequiredService<ILogger<IReferenceCache>>()));
The above registrations replaces services.AddSingleton<IReferenceCache, ReferenceCache>() in ConfigureServices.
You may resolve IMemoryCache the same as you resolve ILogger<IReferenceCache>, i.e. using the GetRequiredService method of the IServiceProvider.
This is documented here by the way.

Create HostingEnvironment instance with WebRootPath

I'm migrating from netcore2.1 to netcore3.1. I have a service where I need to create an IServiceProvider and inject IWebHostEnvironment (I used to inject IHostingEnvironment in netcore2.1). The code used to just create a new Microsoft.Extensions.Hosting.Internal.HostingEnvironment instance but that class doesn't contain WebRootPath anymore. I fetched git repo and tried to look for implementations of IWebHostEnvironment but it seems that the only implementation is internal to aspnetcore which is of no use.
Is there a possibility to create an instance of IWebHostEnvironment somehow?
Daniel
Hi You can access WebRoot Path using in Core 3.1
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
private readonly IWebHostEnvironment _hostingEnv;
public HomeController(IWebHostEnvironment hostingEnv)
{
_hostingEnv = hostingEnv;
}
private Void GetWebRoot()
{
var webRootPath = _hostingEnv.WebRootPath;
}
}
Can you try this, upvote if it helps

How to manage same named constructor

Check my controller code bellow. The thing is i am using first AccountController to access my database context and second one want to access of my appsettings.json file with IConfiguration. But C# not giving to to name same named controller. So how can i access IConfiguration and database context at a time on same Controller file? Please note: I am using .net core mvc 2.1 Picture attached with error. Thanks in advance
Controller code:
public class AccountController : Controller
{
public AppDataCtx DataCtx { get; }
public AccountController(AppDataCtx DataCtx)
{
this.DataCtx = DataCtx;
}
private IConfiguration _configuration;
public IConfiguration AccountController(IConfiguration Configuration)
{
_configuration = Configuration;
}
}

Where to place HttpContext.Current.RewritePath in ASP.NET 5?

I do development in asp.net. Recently I found out that in asp.net 5 there is no Global.asax file.
One of the thing to put in Global.asax file is URL rewriting.
With Global.asax file is gone. Where I can place URL rewriting code. I mean I do something like this in ASP.NET 4.0
HttpContext.Current.RewritePath(...);
I do not want to use URL rewriting modules. I just want to do it using HttpContext.Current.RewritePath method.
My question is where I can put the above code in ASP.NET 5?
Create and add a new middleware at the beginning of Configure method in your Startup (you want it to execute before any other middlewares). Example here
Implement invoke method as follows to do a url rewrite
public Task Invoke(HttpContext context)
{
// modify url
context.Request.Path = new PathString(context.Request.Path.Value + 'whatever');
// continue
return _next(context);
}
I came across this when I was analyzing aspnet/StaticFiles repo on Github.
As an alternative to explicitly creating a middleware class, IApplicationBuilder.Use can be used too:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//...built-in initialization...
app.Use(requestDelegate =>
{
return new RequestDelegate(context =>
{
// modify url (from mbudnik's answer)
context.Request.Path = new PathString(context.Request.Path.Value + 'whatever');
// continue with the pipeline
return requestDelegate(context);
});
});
}
In this case the middlewares are specified directly as instances of Func<RequestDelegate, RequestDelegate>, instead of custom classes.
You need OWIN middlewear here. Because it is the replacement for HttpModules in vNext.
Write below code in Configure method of Startup.cs file
public class Startup
{
public void Configure(IApplicationBuilder app)
{
app.UseMiddleware<MyMiddleware>();
}
}
and Your custom middlewear may look like:
public class MyMiddleware
{
private readonly RequestDelegate _test;
public MyMiddleware(RequestDelegate test)
{
_test = test;
}
public async Task Invoke(HttpContext context)
{
return _test.Invoke(context);
}
}

Categories