MVC: HtmlHelper: Get action/controller values - asp.net-mvc

My project has a HtmlHelper which generates links (with routeValues) which always direct back to the controller/action which 'spawned' them.
Is it possible to retrieve these values from within the HtmlHelper? i.e without supplying them explicitly. This would work fine....
var url = new UrlHelper(html.ViewContext.RequestContext);
anchorBuilder.MergeAttribute("href", url.Action("Details", routeValues));
...were it not for the fact that that action won't always be "Details".

Or this:
var controller = helper.ViewContext.RouteData.Values["Controller"].ToString();
var action = helper.ViewContext.RouteData.Values["Action"].ToString();

Try this:
var currentAction = html.ViewContext.RouteData.GetRequiredString("action");
var url = new UrlHelper(html.ViewContext.RequestContext);
anchorBuilder.MergeAttribute("href", url.Action(currentAction, routeValues));

Related

How can I read and attribute from the controller/Action in Global.asax

In a WebApi 2 application, It's possible to read An attribute of the Action/Controller from the Application_BeginRequest method of the Global.Asax file?
Currently, I'm able to get the controller and action name, but don't know the best way to read a custom attribute from there.
SOLUTION 1
Work with reflection.
CAUTION : this assumes that all your action names are unique for a controller.
If it's not true, I guess... this solution will fail miserably.
First, you need to find the assembly.
This can be done by choosing one of your api's controller, for example (they are of course few other ways to find the needed assembly)
var assembly = typeof (HomeController).Assembly;
Now, I guess you get the controller action and name this way :
var routeData = RouteTable.Routes.GetRouteData(
new HttpContextWrapper(HttpContext.Current));
var actionName = routeData.GetRequiredString("action");
var controllerName = routeData.GetRequiredString("controller");
So next step is to get the type of controller.
Which can be done either by
//you don't need the full name
var controllerType = assembly.GetTypes().FirstOrDefault(m => m.Name == controllerName + "Controller");
or
var controllerType = assembly.GetType("<namespace>." + controllerName + "Controller");
Then you can get the controller's customAttributes
var controllerCustomAttributes = controllerType.GetCustomAttributes();
If you want the action attributes, you'll need to get the method corresponding to your action name.
var actionType = controllerType.GetMethods().FirstOrDefault(x => x.Name == actionName );
then again, to get custom attributes
var actionAttributes = actionType.GetCustomAttributes();
SOLUTION 2
It may be better to add custom ActionFilterAttribute on all of your actions, and work with OnActionExecuting.
See this for example.

MVC EditorFor templates not used when calling controller from another controller

Within an MVC project we have an area with a controller SomeController returning a partial view containing EditorFor statements, each with their own template.
Everything works fine if the controller is invoked directly via a route for that area. However, if it's called via another controller outside of the area, i.e. via 'new SomeController().SomeAction()', the templates are not used, even if explicitly specified (the view is returned ok, but shows just default textboxes etc).
What could be the reason for this and how can it be fixed?
When your action is invoked merely using ctrl.Action(), the current RouteData will be used (with the current area/controller/action values in it) and when Razor tries to resolve your EditorTemplates paths it consults the ViewContext that is still containing the (now wrong) values of the originating action.
You better use the ControllerFactory in order to mimic the desired behavior:
var ctrlFactory = ControllerBuilder.Current.GetControllerFactory();
var routeData = new RouteData();
routeData.DataTokens.Add("area", "target_area_name");
routeData.Values.Add("controller", "target_controller_name");
routeData.Values.Add("action", "target_action_name");
var requestContext = new RequestContext(this.HttpContext, routeData);
var ctrl = ctrlFactory.CreateController(requestContext, "target_controller_name") as TargetControllerType;
if (ctrl != null)
{
ctrl.ControllerContext = new ControllerContext(requestContext, ctrl);
var ctrlDesc = new ReflectedControllerDescriptor(typeof(TargetControllerType));
var actionDesc = ctrlDesc.FindAction(ctrl.ControllerContext, "target_action_name");
var result = actionDesc.Execute(ctrl.ControllerContext, new Dictionary<string, object>()) as ActionResult;
this.RouteData.DataTokens["area"] = "target_area_name";
this.RouteData.Values["controller"] = "target_controller_name";
this.RouteData.Values["action"] = "target_action_name";
return result;
}
See MSDN

How to know which controller/action user was redirected from?

Being inside controller action or view, is there any way to know which controller/action user has been just redirected from? There is Request.UrlReferrer, but it's Url, I need to know Controller and/or action names instead.
There is Request.UrlReferrer, but it's Url, I need to know Controller and/or action names instead.
Given an Url as string you could parse it and obtain the corresponding controller and action names using the following approach:
var url = new Uri("http://example.com/somecontroller/someaction?foo=bar");
var request = new HttpRequest(null, url.AbsoluteUri, url.Query);
var response = new HttpResponse(StringWriter.Null);
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
string controllerName = (string)routeData.Values["controller"];
string actionName = (string)routeData.Values["action"];
Obviously at the place where I used http://example.com/somecontroller/someaction?foo=bar you could use Request.UrlReferrer.AbsoluteUri.
Needless to say that this approach is absolutely unreliable. A much better solution is to simply pass the required information as parameters to the controller action from the calling action.

How to parse a string URL into MVC Route Values (area, controller, action, querystring)?

Is there a method to extract the area, controller, action, and querystring from a URL in ASP.NET MVC? Don't want to reinvent the wheel implementing my own if there's already a way to do it.
Thanks!
I was able to get it from here:
String URL to RouteValueDictionary
To get the area from this example I used:
string area = routeData.DataTokens["area"].ToString();
You could pull this information from the routes:
var controller = RouteData.Values["controller"];
var action = RouteData.Values["action"];
var action = RouteData.Values["area"];
As far as the query string is concerned you could pull it from the Request:
var queryString = Request.Url.Query;
UPDATE:
If the url is coming from a DB:
var uri = new Uri(someStringThatRepresentsTheUrlAndComesFromADb);
var queryString = uri.Query;

String URL to RouteValueDictionary

Is there as easy way to convert string URL to RouteValueDictionary collection? Some method like UrlToRouteValueDictionary(string url).
I need such method because I want to 'parse' URL according to my routes settings, modify some route values and using urlHelper.RouteUrl() generate string URL according to modified RouteValueDictionary collection.
Thanks.
Here is a solution that doesn't require mocking:
var request = new HttpRequest(null, "http://localhost:3333/Home/About", "testvalue=1");
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var values = routeData.Values;
// The following should be true for initial version of mvc app.
values["controller"] == "Home"
values["action"] == "Index"
Hope this helps.
You would need to create a mocked HttpContext as routes constrains requires it.
Here is an example that I use to unit test my routes (it was copied from Pro ASP.Net MVC framework):
RouteCollection routeConfig = new RouteCollection();
MvcApplication.RegisterRoutes(routeConfig);
var mockHttpContext = new MockedHttpContext(url);
RouteData routeData = routeConfig.GetRouteData(mockHttpContext.Object);
// routeData.Values is an instance of RouteValueDictionary
//...
I wouldn't rely on RouteTable.Routes.GetRouteData from previous examples because in that case you risk missing some values (for example if your query string parameters don't fully fit any of registered mapping routes). Also, my version doesn't require mocking a bunch of request/response/context objects.
public static RouteValueDictionary UrlToRouteValueDictionary(string url)
{
int queryPos = url.IndexOf('?');
if (queryPos != -1)
{
string queryString = url.Substring(queryPos + 1);
var valuesCollection = HttpUtility.ParseQueryString(queryString);
return new RouteValueDictionary(valuesCollection.AllKeys.ToDictionary(k => k, k => (object)valuesCollection[k]));
}
return new RouteValueDictionary();
}
Here is a solution that doesn't require instantiating a bunch of new classes.
var httpContext = context.Get<System.Web.HttpContextWrapper>("System.Web.HttpContextBase");
var routeData = System.Web.Routing.RouteTable.Routes.GetRouteData(httpContext);
var values = routeData.Values;
var controller = values["controller"];
var action = values["action"];
The owin context contains an environment that includes the HttpContext.

Resources