I'm curious whether this is possible, or as I suspect, by design not.
In an ASP.NET MVC project I have multiple routes like this:
new Route(
url, // This can be arbitrary
new RouteValueDictionary {
{"area", "MyArea"},
{"controller", "MyController"},
{"action", "Index"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "MyArea"},
},
new MvcRouteHandler()))
I'd like to generate urls (or links) in the (Razor) views used by the actions of MyController. These urls should point to another action of MyController.
Now the problem is, there are multiple routes like above registered under different urls, so simply calling Html.ActionLink() or Url.Action() with the current route values yields a link that points to the url that's route first matches it. That's not necessarily the url the action is currently invoked from.
So basically what I'd like is take the current route and substitute the action with another one. I couldn't find any way to do that.
The urls can be arbitrary, but if necessary, constraints can be applied, e.g. so that the url must contain an action token. Actually all of them currently do, so urls have the following structure:
/AnotherArea/SubSegment/{action} // Routes point from other areas to MyArea/MyController
These urls are there in Html.ViewContext.RouteData.Route.Url of the view, so that action token should be changed somehow when generating a new url. (Well, one could do that with string replacement, but I guess if there is a solution, it should be better than that.)
Thank you for your time!
Now I found a solution, pretty simple:
#Url.RouteUrl("RouteName", new { Action = "OtherAction" })
However this implies the knowledge of the currently used route's name. Since this isn't stored in the Route object itself I opted with the kind of hackish solution of storing the name in the route's DataTokens dictionary. That seemingly doesn't harm and since routes are filled through a service this convention of using DataTokens doesn't need to be kept in mind.
I'm wondering if there's a better solution, though.
Related
I am currently on the page /Customer/Edit/13244.
When I use #Url.Action("Edit", "Customer") on the same page it returns me /Customer/Edit/13244, but I want it to return /Customer/Edit/.
Kindly tell me how to fix this issue.
This is a "feature" of MVC that many people find unnatural and was previously reported as a bug.
Microsoft's official response:
Ultimately if you want the most control over what gets generated for a URL there are a few options to consider:
Use named routes to ensure that only the route you want will get used to generate the URL (this is often a good practice, though it won't help in this particular scenario)
Specify all route parameters explicitly - even the values that you want to be empty. That is one way to solve this particular problem.
Instead of using Routing to generate the URLs, you can use Razor's ~/ syntax or call Url.Content("~/someurl") to ensure that no extra (or unexpected) processing will happen to the URL you're trying to generate.
Actually, this bug only rears its ugly head when you try to re-purpose an action method name. If you use a different action method name other than Edit in the case where it is not followed by id, this problem will magically disappear.
You will need to use (assuming your using the default route with id = UrlParameter.Optional
#Url.Action("Edit", "Customer", new { id = "" })
Is it possible to show routeName somehow in Html.Action
Something like this
Html.Action("Languages", "UIHelper", new {routeName="Default"});
Currently my application has many areas, and Html.Action is used in main Layout. When I redirect user to area page then my Html.Action could not find action/controller because mvc try to find it in current (redirected) area
There is no Html.Action() extension method that would make a use of a route name. Of course, you can go and create your own extension method that suits your needs, which I recommend doing anyway as a good exercise, but in your case solution might be much simpler.
By default, all routes use the current area for matching. You can override area by providing Area parameter, like this:
#Html.ActionLink("MyLink", "MyAction", new {Area = "MyArea" });
In your case, you want to use a controller in the root area, which is simply represented as a blank string. So all you need to do is:
#Html.Action("Languages", "UIHelper", new { Area = "" });
SUMMARY:
I'm reviewing the route mapping for a site I've recently begun work on. I've encountered a route that I'm not familiar with:
RouteTable.Routes.MapRoute(NamedRoutes.ROUTE_NAME, "urlSegment1/urlSegment2", new { });
Notice that the "default" parameter for MapRoute is an empty object. This is normally where I would specify my controller, action, and any parameters. I've been googling around and am finding that I'm not asking the right questions to produce the answer I'm looking for.
QUESTION:
How does MVC routing behave when the "defaults" parameter of MapRoute is an empty object? An answer would be great. Supporting docs would be even better.
EDIT:
The actual route being used is:
routes.MapRoute(NamedRoutes.BROWSE_MEN, "browse/Mens", new { } });
And the problem is occurring when generating the URL using:
Html.BeginForm("Add", "Signup", FormMethod.Post, new { id = "signup", enctype = "multipart/form-data" })
They are mapped as stings and in this case matches everything. The defaults are only there if one of the controller/action parameters are not supplied by the url. In this case no defaults are meaningless. It is actually catching everything string/string.
The route you have there is will match a request that contains exactly the two segments in the url ex. http://localhost/urlSegment1/urlSegment2 and returns a 404. I guess you have this route defined to avoid the following routes handle this request.
When you don't specify a controller, either as URL parameter (token) or default value, you should get an InvalidOperationException:
The matched route does not include a 'controller' route value, which
is required.
Same for action. That is for incoming request. For URL generation there's no requirement for controller or action.
You don't have to specify defaults.
So it works as if there were no default object specified
Is it possible to use routing and query strings together?
An example would be the following as a route in my Global.asax file:
void RegisterRoutes(System.Web.Routing.RouteCollection routes)
{
routes.MapPageRoute("My Route Name", "users/{UserName}", "~/users/UserInfo.aspx");
}
Could I somehow use http://www.mywebsite.com/users/usernamehere?info=bla and pass info=bla to the page? I would rather not try to encode this in the route schema.
Of course you can use routes and query string values together. When you define a route, your route is not determined by the query string; rather, it's determined by your URL parts.
Check out this concise article about using routes and query strings -- Sanderson points out that you can easily use the two together.
And even though the referenced article is about MVC2, you can use routing with ASP.NET web forms. I've used it before and it's not too hard to implement.
I was wondering whether there is a way to create an ActionLink or similar, that changes only a few parameters of the actual query, and keeps all the other parameters intact. For example if I'm on an URL like http://example.com/Posts/Index?Page=5&OrderBy=Name&OrderDesc=True I want to change only the Page, or OrderBy parameter and keep all other parameters the same, even those I don't yet know of (like when I want to add a Search parameter or something similar too).
The header of my current action looks like this:
public ActionResult Index(int? Page, string OrderBy, bool? Desc)
and I'm only interested in the values that this controller "eats". I want however that when I extend this action (for example with a string Search parameter) the links should work the same way as before.
Here is what I did already:
Create a new RouteValueDictionary and fill it with everything from RouteData.Values
Problem: This only fills the parameters that are used in the Routing, so all other optional parameters (like Page) to the controller are lost
Add everything from HttpContext.Request.QueryString to the previous dictionary
This is what I am currently using
Problem: It might have some junk stuff, that the Controller didn`t ask for, and it doesn't work if the page was loaded using POST. You also don't have any ModelBindings (but this isn't much of a problem, because we are re-sending everything anyway)
Use HttpContext.Request.Params
Problem: this has too much junk data which imho one shouldn't add to a RouteValueDictionary that is passed to an ActionLink
So the questions:
Is there an RVD that has all the data that was passed to the Controller and was used by it?
Is this solution good, or are there any caveats I didn't think about (mainly in the context of changing a few query parameters while keeping the others intact)?
Is there a way to filter out the "junk" data from the Params object?
EDIT: Checked the RouteData.DataTokens variable, but it's usually empty, and doesn't contain everything I need. It seems to only contain parameters that are needed for the routing somewhere, but not all of the parameters.
Have a look in RouteData.DataTokens.
RouteData.DataTokens # MSDN documentation:
Gets a collection of custom values that are passed to the route handler but are not used when ASP.NET routing determines whether the route matches a request.
HTHs,
Charles