Reputation: 2025
In my app, I am checking if some config file is available or not, if it's not then I want to redirect to install page.
To me the best place to accomplish this is application_start
. Because it's happening for only one time. If I do the checking in application_start
and write Response.Redirect
I will get Response is not available in this context
.
I tried other answers in stack overflow to redirect in application_start
like HttpContext.Current.Response.Redirect
; none worked for me.
I don't want to do it in a base controller
or a filter
because the checking logic will happen for every single request.
My goal is to check it only once and it's best to be when the app start.
Update 1
I added response.redirect to the application_start but got error like this:
application start:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Response.RedirectToRoute(
new RouteValueDictionary {
{ "Controller", "Home" },
{ "Action", "about" }
});
}
but i am receiving an error like this:
Response is not available in this context.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Web.HttpException: Response is not available in this context.
Upvotes: 9
Views: 1855
Reputation: 18974
If you need this feature for a specific page, use cookies like bellow inside the action:
public ActionResult Index()
{
string cookieName = "NotFirstTime";
if(this.ControllerContext.HttpContext.Request.Cookies.AllKeys.Contains(cookieName))
// not first time
return View();
else
{
// first time
// add a cookie.
HttpCookie cookie = new HttpCookie(cookieName);
cookie.Value = "anything you like: date etc.";
this.ControllerContext.HttpContext.Response.Cookies.Add(cookie);
// redirect to the page for first time visit.
return View("FirstTime");
}
}
If you want to use it for each action method, You need to write an ActionFilterAttribute
to call it every time needed.
Note that the Request.Context
is not longer available to the Application_Start
event
Upvotes: 0
Reputation: 541
If you really want to avoid having a filter run for every request after setup then you can do something like this:
RedirectAttribute.cs (generic example)
public class RedirectAttribute : ActionFilterAttribute
{
private readonly string _controller;
private readonly string _action;
public RedirectAttribute(string controller, string action)
{
_controller = controller;
_action = action;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.ActionName != _action ||
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName != _controller)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(new {controller = _controller, action = _action})
);
}
}
}
In Global.asax.cs above "FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);"
if (/*Insert logic to check if the config file does NOT exist*/)
{
//Replace "Setup" and "Index" with your setup controller and action below
GlobalFilters.Filters.Add(new RedirectAttribute("Setup", "Index"));
}
Now, after your user has fully completed setup, you can unload the app domain:
HttpRuntime.UnloadAppDomain();
Please note: you will need to make sure that your app has permission to unload the AppDomain. If it does not, you can try File.SetLastWriteTimeUtc(...) on the configuration file (AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.) This will also unload the AppDomain.
Unloading the AppDomain will "restart" the web app and call Application_Start() again. The filter will not be added to your requests since your if statement will determine that the app has already been configured.
Upvotes: 3
Reputation: 983
As a workaround, you could use lazy initialization in a static variable inside a filter. The actual file operations to check for the config file will only happen once during the first request. After that the value of the check for the config file is saved in the static Lazy
variable. As an added bonus it's also threadsafe.
In the end the check still happens on every request, but the operation is fast after the initial check because the result of the check is saved in memory.
public class ConfigFileCheckAttribute : ActionFilterAttribute
{
//Lazy<> is threadsafe and will call the Func in the constructor just once
private static Lazy<bool> _configFileExists = new Lazy<bool>(ConfigFileExists);
private static bool ConfigFileExists()
{
//Logic to check for config file here
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!_configFileExists.Value)
{
//set your redirect here
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Setup", action = "Configure" }));
}
}
}
The last bit is to register the filter in your App_Start/FilterConfig.cs:
public static class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//...
filters.Add(new ConfigFileCheckAttribute());
}
}
Upvotes: 0