I have the following requirement:
On every request to my web page, regardless of which action the user is trying to invoke, I need to call some code that checks if a resource is in place. If it is, then everything is fine and the action method should be called as normal.
However, if this resource is not available, I want all requests to return a separate page asking the user to select another resource from a list of available ones.
So is it possible to have one method run before any action method that have the option of cancelling the call to the action method, and doing something else instead?
Look at global action filters (available since asp.net mvc 3): http://msdn.microsoft.com/en-us/library/gg416513%28v=vs.98%29.aspx
Basically, in your Global.asax, you can register the filter globally during your application startup (in Application_Start()) with:
GlobalFilters.Filters.Add(new MyActionFilterAttribute());
You can then override the OnActionExecuting method, and set the Result property with a RedirectToRouteResult.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (IsMyResourceAvailable())
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {
{ "Controller", "YourControllerName" },
{ "Action", "YourAction" }
});
}
base.OnActionExecuting(filterContext);
}
MVC provides several hooks to do this.
In a base controller, you can override Controller.OnActionExecuting(context) which fires right before the action executes. You can set context.Result to any ActionResult (such as RedirectToAction) to override the action.
Alternatively, you can create an ActionFilterAttribute, and exactly like above, you override the OnActionExecuting method. Then, you just apply the attribute to any controller that needs it.
Related
I have an ASP.Net MVC 5 web site and need to dynamically re-route all incoming requests to a specific Controller and Action under certain circumstances.
For example, if the database does not exist, I would like to re-route all incoming requests to a specific Action, such as SetupController.MissingDatabase:
public class SetupController : Controller
{
public ActionResult MissingDatabase()
{
return View();
}
}
I would like to check for this condition (database existence) for each and every request. It would be best to perform the check early on in the pipeline, rather than at the top of each Action, or in each Contoller. Of course, if the incoming request is being routed to SetupController.MissingDatabase I don't need to perform the check or re-route the request.
What is the best way to accomplish this?
Specifically, where in the ASP.Net MVC 5 pipeline is the best place to perform such a check, and how would I re-route the incoming request?
You can create an action filter to do that and register it in the application level so that it will be executed for all the incoming requests.
public class VerifySetupIsGood : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var dbExists=VerifyDataBaseExists();
if ( ! dbExists)
{
var values = new Dictionary<string, string> {{"action", "MissingDatabase"},
{"controller", "Setup"}};
var routeValDict = new RouteValueDictionary(values);
//redirect the request to MissingDatabase action method.
context.Result = new RedirectToRouteResult(routeValDict);
}
}
}
Assuming VerifyDataBaseExists() executes your code to check the db exists or not and return a boolean value. Now register this action filter in the Application_Start event in global.asax.cs
protected void Application_Start()
{
//Existing code goes here
GlobalFilters.Filters.Add(new VerifySetupIsGood());
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
//Existing code goes here
}
You can update the action filter to check for a subset of requests as well if needed. You may get the request url from the object of ActionExecutingContext and use it to do your checks.
Take for instance the following code for Edit action in my controller:
// POST: /Admin/Text/Edit/5
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Edit(TextViewModel editing)
{
if (!ModelState.IsValid)
return View(editing);
Text existing = _repository.Find(editing.Id);
if (TryUpdateModel(existing)) {
_repository.Update(existing);
if(Request.IsAjaxRequest())
return Content(bool.TrueString);
else
return RedirectToAction(pndng.Admin.Text.List());
}
else return View(editing);
}
What I want this action to do is handle both classic (non AJAX form) and AJAX (jquery) POSTs. In case of AJAX POST it is very likely that the request is coming from an inline edit form. In this case, the action should just return the Content=Ok result. In case we have been editing the model in a form page and performed a classic postback we want to redirect the user back to the content List (see RedirectToAction()).
However, the thing bothering me is the if..else clause. I would like to abstract this away into an action filter/attribute. I would like to leave in the redirection call (as this is the default) but have the action filter to act upon it if it detects that the request was in fact AJAX request, meaning stop the redirection and just return the ContentResult or JsonResult.
Also feel free to respond if you think my workflow is wrong.
You should be to implement a custom ResultFilter to do what you want, by implementing IResultFilter. something like:
public class AjaxOverrideFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Result is RedirectResult)
filterContext.Result = new ContentResult {Content = "Ok"};
}
}
And then decorate that action with [AjaxOverrideFilter].
This should override the result if its an ajax request and the result type was a redirect... At least, this should give you a push in the right direction. I'm not wildly convinced that this is a great architectural approach though...
I need to prevent users logging into my ASP.NET MVC application from multiple sessions, and found this answer how to do it.
Now I want to add an MVC twist: some of the public methods on the Controller are unprotected, and I don't care who accesses them, and some are protected by an [Authorize] attribute to ensure that only logged-in users can access them. Now I want to customize the AuthorizeAttribute so that all methods flagged with that attribute will do the no-multiple-login verification described in the related question, and throw some kind of LoggedInElsewhereException so that the client can understand if and why the check failed.
I'm sure it can be done, but how?
Just derive your new attribute from AuthorizeAttribute and override OnAuthorization method. In the method do your "single session" checks first, then fall back to base implementation.
E.g.
public class CheckSessionAndAuthorizeAttribute : AuthorizeAttribute
{
public override OnAuthorization(AuthorizationContext context)
{
//check session id in cache or database
bool isSessionOK = CheckSession();
if (!isSessionOK)
{
//can be View, Redirect or absolutely customized logic
context.Result = new MyCustomResultThatExplainsError();
return;
}
//do base stuff
base.OnAuthorization(context);
}
}
Hi I have some codes that need to run for 1 time for a request. I have a BaseController which all controllers derived from. I write my code to BaseController onActionExecuting method but it is not good because for every action execution code is running. I can prenvent it with a basic if clause but i dont want to use it like that.
What is the best place for run a code 1 time for a request. I also want to reach HttpContext where i write this code. thanks
After your comment about child actions you could test if the current action is a child action and don't execute the code. So you could have a custom action filter:
public class CustomFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// this method happens before calling the action method
if (!filterContext.IsChildAction)
{
// this is not the a child action => do the processing
}
base.OnActionExecuting(filterContext);
}
}
and then decorate your base controller with this custom attribute. Similar test could be performed in the overridden OnActionExecuting method of your base controller if you prefer it instead of action attributes:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.IsChildAction)
{
// this is not the a child action => do the processing
}
base.OnActionExecuting(filterContext);
}
Feel free to close this one if it s a duplicate. I couldn't find an answer.
I wish to be able to place a System.Web.ActionFilterAttribute on an Action Method and override the OnActionExecuting method to insert business logic which determines if the Action should be fulfilled.
Can the ActionExecutingContext be used to cancel the executing Action Method and do one of the following:
Send an HTTP Status Code (and the corresponding <customError> page).
Execute a different Action Method within the same Controller.
Send an HTTP Status Code (and the
corresponding <customError> page)
Almost:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.StatusCode = 500;
}
Execute a different Action Method
within the same Controller.
Yes:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Result = new ViewResult() { ViewName = "SomeOtherAction" };
}
You can always redirect to another controller/action in an action filter.
See here for an example.