Redirect user from startup.cs asp.net core
My suggestion is to use a Middleware solution that can handle just what you are looking for.
What is middleware
Middleware is software that is assembled into an application pipeline to handle requests and responses.
Each component:
Chooses whether to pass the request to the next component in the pipeline. Can perform work before and after the next component in the pipeline is invoked.
Here is a simple implemetation of how the middleware would work.
Now as we can assume that only at startup of the application we need to check if the database is installed and not every request to the application we can add the middleware only if it is needed. Therefore on restarts the middleware is only included if the database is not installed. So we will only register the Middleware extension when the database is not installed at startup.
The follow code assumes you have reviewed the linked MSDN page above.
Here is a simple example of the middleware (I called it DatabaseInstalledMiddleware
)
public class DatabaseInstalledMiddleware
{
public DatabaseInstalledMiddleware(RequestDelegate next)
{
_next = next;
}
readonly RequestDelegate _next;
public async Task Invoke(HttpContext context, IConfiguration configuration)
{
if (!IsDatabaseInstalled(configuration))
{
var url = "/databasechecker";
//check if the current url contains the path of the installation url
if (context.Request.Path.Value.IndexOf(url, StringComparison.CurrentCultureIgnoreCase) == -1)
{
//redirect to another location if not ready
context.Response.Redirect(url);
return;
}
}
//or call the next middleware in the request pipeline
await _next(context);
}
public static bool IsDatabaseInstalled(IConfiguration configuration)
{
var key = configuration["SQLConnectionSettings:SqlServerIp"];
return !string.IsNullOrEmpty(key);
}
}
The code is pretty basic, however we see that Invoke
method accepts the current HttpContext
and injects the IConfiguration
instance. Next we run a simple method to check if the database is installed IsDataBaseInstalled
.
If this method returns false
we check to see if the user is currently requesting the databasechecker
url. You can change this pattern or add urls as required if there are multiple install urls. If they are not requesting an install URL then we redirect them to the /databasechecker
url. Otherwise the Middleware executes as expected with the line await _next(context)
.
Now to use this in your request pipleline simply add the middleware before the MVC middleware such as.
This is in the startup.cs
file. (Below is the basic startup file)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
if (!DatabaseInstalledMiddleware.IsDatabaseInstalled(Configuration))
app.UseMiddleware<DatabaseInstalledMiddleware>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Raj
Updated on June 05, 2022Comments
-
Raj almost 2 years
I have a requirement where I want to check if database is connected(I have the class for that). if this class's method returns false then I want to redirect to database page/view where setup will take place. I have Asp.Net core identity. I want to check this condition before EF core tries to connect to DB. I tried to use the following but returns "too many redirects in browser". Note: Every method in home controller has [Authorize] except DatabaseCheck. Once the user is redirect here I would take values and update Appsettings.json, and application would run normally. Thanks for insights.
public void ConfigureServices(IServiceCollection services) { services.AddSingleton(Configuration); services.AddDbContext<MyContext>(options => options.UseSqlServer(SqlSettingsProvider.GetSqlConnection())); services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<MyContext>() .AddDefaultTokenProviders(); services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/LogIn"); services.AddMvc() .AddJsonOptions(options => { options.SerializerSettings.Formatting = Formatting.Indented; }).AddJsonOptions(options => { options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; }); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); } else { app.UseExceptionHandler("/Home/Error"); } if (!DatabaseInstalledMiddleware.IsDatabaseInstalled(Configuration)) app.UseMiddleware<DatabaseInstalledMiddleware>(); app.UseStatusCodePagesWithReExecute("/StatusCodes/{0}"); app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); routes.MapRoute( name: "defaultApi", template: "api/v2/{controller}/{id?}"); }); }
-
Raj over 6 yearsThanks Nico, it returns error : SqlException: Login failed for user ''." . Looks like EF core kicks in before anything else. I am using EF core and Identity. Any suggestion, I have updated my methods above.
-
Nico over 6 years@Raj its hard to determine where the error is coming from, however the error is likely due to EF trying to execute a query on the database. The stack trace would be handy to see where in the code the error occurs. It may be something very simple as trying to show the logged in users name or some sort of database stored setting.
-
Raj over 6 yearsThat's the logic I wanted in place if connect string is set then EF should not kick in , I guess it will because of configureService DP.
-
Nico over 6 years@Raj if you can post your stack trace of the error we can probably figure out exactly why and where its happening.
-
Raj over 6 yearsThanks Nico. I uploaded the error image above. EF core is reaching/seeking out for database before regardless of the middleware. Can I redirect from program.cs ?