I'm working on a menu-generating HtmlHelper extension method. This method will need to know which Action is being executed. So if Home/Index is executing, the extension method would show all links to other actions that're "coordinated." In a sense, all I need to know during the execution of the Home controller's Index action is the name of the Controller and the name of the Action that are being executed so that other logic can be executed. Is this possible?
Try this
var action = HtmlHelper.ViewContext.RouteData.Values["action"];
var controller = HtmlHelper.ViewContext.RouteData.Values["controller"];
I do something similar with a filter attribute. You can get the action name like this:
filterContext.RouteData.Values["action"].ToString();
I use this to disable the menu item that represents the current context.
I need something like this, but not quite. I would love to be able to get some strongly typed way of knowing which action is executing.
So clarify in doing AOP where i only allow access to a given action if the user has rights for that action.
The problem with using a string for determining which rule to check for, is that if some developer renames an action, I wont get a compile error telling me that my rule is broken.
Related
I'm trying to find out if its possible to create a controller or action result in MVC that will trigger for every action.
Basically I want a check to occur before people are directed to the url each time, if this check fails I will return a different view but I don't want to write this out for every single action in my controller.
Can this be done??
This can be done with a custom ActionFilterAttribute.
Overriding OnActionExecuting means I can put the same check in for all actions.
I would like to know the differences between a custom action filter and a custom action selector in ASP.NET MVC.
Say we want to restrict who can have access to an action method on a controller based on some rules. I could either create an action filter extending the ActionFilterAttribute class or extending the ActionMethodSelectionAttribute class, so that I could have something like:
[MyRestriction]
public ActionResult AnyAction(){}
Could anyone explain the differences between them so that I can make the right decision?
If you look at the documentation for ActionMethodSelectionAttribute, you will see at the very bottom of the page that there are a number of other classes that derive from this attribute.
These include:
Microsoft.Web.Mvc.AjaxOnlyAttribute
System.Web.Mvc.AcceptVerbsAttribute
System.Web.Mvc.HttpDeleteAttribute
System.Web.Mvc.HttpGetAttribute
System.Web.Mvc.HttpHeadAttribute
System.Web.Mvc.HttpOptionsAttribute
System.Web.Mvc.HttpPatchAttribute
System.Web.Mvc.HttpPostAttribute
System.Web.Mvc.HttpPutAttribute
System.Web.Mvc.NonActionAttribute
In other words, these are the attributes which control which Action Method is selected during routing when there are several different choices to choose from (ie there are 2 different Index methods, one decorated with [HttpGet] and one with [HttpPost]).
ActionFilterAttribute, on the other hand, is called only when an action is actually executing.
Think about it this way, The selection can run even if the action doesn't execute, the ActionFilter only runs if it does. The selection filter is only used to determine whether the action is a match condition, the action filter is used to do some action before, after, etc.. the action or response is executed.
I'm currently using MVC5.
Imagine the scenario where one controller ActionA does its work and the redirects to another controller ActionB but also wants this second method to display a message on its related view.
If Controller ActionA sets the ViewBag.Message and then calls RedirectToAction, when ActionB starts, the value of that Message is gone.
What's the best way to pass a message from one action controller to another, without using Session ??
You can use TempData:
Action A:
TempData["Message"] = "Hi";
Action B:
var message = TempData["Message"];
Once you call the getter in Action B, the information will be automatically removed from memory.
This article is a really good explanation of the various persistence techniques available in ASP.NET MVC.
You say no session, but TempData can be a good place depending on what you're passing. It's essentially Session except that the item will be removed from Session once you've accessed it. TempData actually uses session by default unless you write your own provider.
Passing it as an option in the query string, or passing it as a parameter in the routing (similar to what AJ says)
Other than that you've got the standard run of the mill options that are provided in ASP.NET. HttpContext.Items or maybe HttpContext.Cache. But both of those are shared across the entire application domain so management can get tricky.
Remember, web is supposed to be stateless. So if it's really important for that message to get there, you probably want to put it in the URL somehow (query string or routing), or use a database.
I would put an optional argument on the method signature for ActionB, for example:
public ActionResult ActionB(string message = "")
Note that optional parameters need to be last in your parameters list.
I need that every action in a controller checks for a specific condition.
If that condition is not met, the user must be redirected a specific action.
How do I do this without having to check the result of that condition in every action?
While writing this, it occourred to me that I could user an Attribute like the AuthorizeAttribute.
Do you have any thoughts on this? Is this a good idea?
You can create a custom class ActionFilterAttribute that overrides the OnActionExecuting method. You can use the Result property for the ActionFilterContext to have it redirect to the appropriate View.
I'd like to get access to the current executing Controller so I can offload the return of the appropriate ActionResult onto a helper method. To this end, I'm looking for the equivalent of what I would have thought would be ControllerContext.Current but isn't. Thanks!
Edit for clarification: I've got a generic form control which is JavaScript-based but I'd like to add an option so that it works with noscript. At the moment my Controller sets the ViewData.Model to a JSON-ified Models.FormResponse<T>.
This FormReponse is set up with the status of the post and any error messages that were generated, so I'd like a GetActionResult() method which does the script/noscript check (a hidden form input) and either:
Sets the Model to the JSONed FormResponse and returns a View(), or
Serializes the FormResponse to the Session and returns a Redirect().
As this obviously changes the return value and I don't want to do the check myself every time, I need to call View or Redirect from the FormResponse's GetActionResult method in order to call this as:
return formResponse.GetActionResult();
I know with a more astronautical design this could be made even more robust but as the noscript option is not a major feature at the moment, I just need to get a quick solution working that doesn't break other things.
Update #2
The following, implemented in an ActionResult class, does the job for me. Thanks CVertex!
public override void ExecuteResult(ControllerContext context)
{
if (CMSEnvironment.NoScript)
{
Oracle.Response.Redirect(Oracle.Request.UrlReferrer.ToString(), true);
}
context.Controller.ViewData.Model = _model.ToJSON();
new ViewResult()
{
ViewName = Areas.Site.Helpers.SharedView.Service,
ViewData = context.Controller.ViewData
}.ExecuteResult(context);
}
Statics are bad for testability, and very much discouraged in MVC.
Why do you want to access the current controller and action method?
The best way to do this is to implement your own ActionFilter.
This gives you a means of intercepting requests before or after actions methods execute.
EDIT:
By intercepting the result inside OnActionExecuted of a filter, you can do your noscript/script checks and modify your ViewData accordingly for consumption by the View.
Inside OnActionExecuted, you can also do the noscript check and have complete control over the final ActionResult or the ViewData, as you please.
Or, you can write your own ActionResult that makes all these decisions.
So, your controller action ultimately does
return new MyActionResult(format_and_view_agnostic_model_object);
There doesn't appear to be a way to navigate to the current Controller from a thread. That is you could get the ControllerBuilder and you can get the MvcHttpHandler but neither then lets you access the controller instance that the handler is using.