Dynamic ASP.net MVC Routing - c#

I have a need for some dynamic routing. So my routes would look like this:
{UserName}
{UserName}/Edit/{id}
{UserName}/Delete/{id}
Where the users would be routed to the user controller. But I still want to maintain routes to controls like:
{Controller}/Edit/{id}
{Controller}/Delete/{id}
So basically I want it to direct to the physical controller say called OrdersController for edit delete but if someone navigates to /jdoe/ it sends it to the user controller.
How do I do this in my routes?

You need to create multiple routes, and keep them in the appropriate order
// one route for Users
routes.MapRoute("Users",
"{username}/{action}/{id}",
new { controller = "Users", action = "Index", username = string.Empty, id = UrlParameter.Optional },
new { id = #"\d+" }
);
// one route for everything else
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional},
new { id = #"\d+" }
);
It's not "really" as easy as above, but that's the jist of it. You would need too add a RouteConstraint to validate usernames.
Lastly, if you're using the username parameter, then why do you need the id? Just a thought.
Aside:
If you look at the user section here on StackOverflow, you'll see the routing look more like this.
users/{id}/{username}
users/{id}/edit
users/{id}/delete
I would personally say that this is a lot less work to achieve... but hey, that's just me.

Related

MVC5 Routing with two parameters in an Area

I have an area called News, and within that area I have a Post Controller. The post controller has the following action:
public ActionResult Index(int id, string name)
{
...
}
I have a route in the NewsAreaRegistration
context.MapRoute(
"News_post",
"News/{controller}/{id}/{name}",
new { action = "Index", controller = "Post",name = UrlParameter.Optional }, new { id = #"\d+"}
);
I have also tried without the name object attribute, with and without the id #"\d+ .. Thing is I have this on another site with the exact same setup, just so confused at why it's not working as expected.
Now firstly, the action will resolve:
http://example.com/News/Post/Index/3
When I want it to resolve to
http://example.com/News/Post/3
And then I also want this string parameter at the end so it should resolve to
http://example.com/News/Post/3/test-post
but instead resolves to
http://example.com/News/Post/Index/3?test-post
I am having a total nightmare with this routing stuff. I have tried to mess around with the Routing attributes but also have no luck there with areas... Any ideas guys?
So I fixed part of the issue by making the Route look like this:
context.MapRoute(
"News",
"News/Post/{id}/{name}",
new { action = "Index", controller = "Post" },
new { id = #"\d+" }
);
So I removed the UrlParamter.Optional for the name argument. I need to really get my head around how some of these routes work. At times it is so easy, but then others I can't get it to do the simplest things.

Custom Routing without changing the URL

I have a controller and a view which returns book information when I pass the ID, e.g.:
/Content/Index?id=1
Now I want to make this as a friendly URL to end user. For eg:
Books/BookName (Name of the book the Id 1 is mapped to)
So I added a route values in global.asax as :
route.maprRoute(name:"custom", url:"Books/{bookname}",
defaults: new {controller = "bookMap", action ="index"}
in "BookMap" controller I get the bookname and convert that to the ID (which is 1)
and do a redirectionToAction to Content/Index by passing the ID as a parameter.
This works fine. But the problem is I want to keep the friendly name after redirecting to the view. Now it changes to Content/Index?id=1. But I want to keep the friendly URL which is Books/BookName. How do I achieve this pls.
You can use HttpContext.RewritePath(url) to do a redirect "internally" which keeps the external URL. Use this in place of the RedirectToAction. Note however that it's not properly supported by the MVC framework at this time so it will be a little "hacky" to implement.
You should change your route for this:
routes.MapRoute(
"Books",
"book/{bookname}/{bookid}",
new { controller = "book", action = "Index", bookname = UrlParameter.Optional },
new { id = #"\d+" }
);

How To Prevent Multiple ASP.NET MVC Route Mappings

I have an ASP.NET MVC routing question. First, let me explain my areas setup. It's quite simple.
Areas
|
+--Foo
|
+--Controllers
|
+--BarController.cs
I have a folder in my areas called "Foo" and controller called "BarController.cs" The Bar controller has several methods named "DoStuff1()", "DoStuff2()", etc.
My website uses the following URLs:
/foo/bar/15
/foo/bar/dostuff1
/foo/bar/dostuff2
The first URL requires an id and uses the default Index() method in the Bar controller to populate the webpage with a view and model.
In the second and third URLs, I'm using them for jQuery ajax calls.
Here is the code from my area registrion
context.MapRoute(null, "Foo/Bar/DoStuff1", new
{
action = "DoStuff1",
controller = "Bar"
});
context.MapRoute(null, "Foo/Bar/DoStuff2", new
{
action = "DoStuff2",
controller = "Bar"
});
context.MapRoute(null, "Foo/Bar/{id}", new
{
action = "Index",
controller = "Bar"
});
My problem is that for each new controller method I create, I have to add another route mapping in the area registrion file. For example, if I add the method DoStuff3(), I'll need to add this to the area registration:
context.MapRoute(null, "Foo/Bar/DoStuff3", new
{
action = "DoStuff3",
controller = "Bar"
});
How can I create a generic route mapping to handle the URLs I mentioned above that doesn't require new additions to the area registration file for new controller methods?
You can pull out the controller action.
Write the URL like this:
"Foo/Bar/{action}"
Additionally, you can pull out the controller as well, and write
"Foo/{controller}/{action}"
In this case, action = "Index" provides a default value of "Index" if no action parameter is provided.
In this case, you need to disambiguate between "Foo/Bar/{action}" and "Foo/Bar/{id}". Since matching is done in order, you'll want to put the id route first, and add a numeric constraint to the id parameter. This allows valid numeric ids to match it, and action names to skip down to the next route. Your two routes would look like this:
context.MapRoute(null, "Foo/Bar/{id}", new
{
action = "Index",
controller = "Bar"
},
new { id = #"\d+" });
context.MapRoute(null, "Foo/Bar/{action}", new
{
action = "Index", //optional default parameter, makes the route fall back to Index if no action is provided
controller = "Bar"
});
The default routing that comes with MVC templates are good for most of the needed route configurations.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Url.Action doesnt work with non standard MVC Route

I am having trouble making #Url.Action work with Area's that have a non standard route structure.
For instance if I register this route in my Dashboard area:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}",
new { controller = "View", action = "Display", id = UrlParameter.Optional }
);
and then in my layout view I call:
#Url.Action("Select", "View", new { area = "Dashboard" })
I get a proper url: /Dashboard/View/Select
However, if I change the route to include an optional secondary id like this:
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display", id = UrlParameter.Optional, secondaryid = UrlParameter.Optional }
);
Then the same call to #Url.Action(...) doesn't return any url. If I specify those optional parameters with real values like so:
#Url.Action("Select", "View", new { area = "Dashboard", id = 1, secondaryid = 2 })
I do get a god return value of: /Dashboard/View/Select/1/2
The problem is that for some of my actions in this area don't need the id or secondary id and I want the url to be generated without them. If i set them to (int?)null it still doesn't work.
Am I doing something wrong? Shouldn't Url.Action(...) return the URL without the id and secondaryid tokens if I dont specify them in the routeValues parameter?
Having multiple optional parameters does funky things to your routes. Basically, the route engine cannot (has trouble) matching one or no optional parameters where there is a group of them. For more information, check out this blog post on the same issue.
Since you don't always need id or secondary id, just make a couple of routes to handle those cases.
context.MapRoute(
"Dashboard_IdAndSecondaryId",
"Dashboard/{controller}/{action}/{id}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default_WithSecondaryId",
"Dashboard/{controller}/{action}/{secondaryid}",
new { controller = "View", action = "Display"}
);
context.MapRoute(
"Dashboard_default",
"Dashboard/{controller}/{action}/{id}/",
new { controller = "View", action = "Display", id = UrlParameter.Optional}
);
Now, when you send just an Id, just a secondaryId or both, you will have routes that will match. We can remove the optional parameter declarations in the first two routes, because in order to match that route, you would have to send the required parameters. Without sending the required parameters, you would want only the last route to match.
The last route is your default route when only Id or none is sent in the action link. I believe that this order works as well, keeping in mind you want your routes to go from most specific to least specific given that they are processed in order.

.NET MVC 3 Routes

Recently I've made the transition from Web Forms to MVC 3 and I've been trying to get to grips with MVC routes. I have a somewhat peculiar problem in that when I receive a request to my application (e.g. subdomain1.organisation.com or subdomain2.organisation.com) I wish the default route to be used as follows:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
However, when a request is received by my application through a particular subdomain e.g. subdomain3.organisation.com, I want the application to default to a particular controller. I've been following code at:
http://blog.maartenballiauw.be/post/2009/05/20/ASPNET-MVC-Domain-Routing.aspx
which should what I want. So the code in my Global.asax is as follows:
routes.Add("DomainRoute", new DomainRoute(
"subdomain3.organisation.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Subdomain3Controller", action = "Index", id = "" }
));
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
When deployed, my application behaves correctly when requests are sent to subdomain3.organisation.com, using Subdomain3Controller. However, when visiting any other subdomain e.g. localhost/Subdomain3Controller/Index my application seems to select the incorrect route.
My form helpers appear to return an incorrect value for:
ViewContext.Controller.ValueProvider.GetValue("controller").AttemptedValue
#using(Html.BeginForm(ViewContext.Controller.ValueProvider.GetValue("action").AttemptedValue, ViewContext.Controller.ValueProvider.GetValue("controller").AttemptedValue, FormMethod.Post, new Dictionary<string, object> {{ "id", "formid" } })){
Any ideas why this might be? Any light that anyone could shed on this issue would be much appreciated.
Many thanks in advance.
Have you tried the routing debugger, which is designed to help debug these sorts of problems
Looks like it's installable via Nuget now too.
I've also noticed that if I hard-code the controller name into the BeginForm helper, the incorrect action value is still returned:
using (Html.BeginForm("Index", "Subdomain3Controller", FormMethod.Post, new Dictionary<string, object> { { "id", "formid" } }))
{
generates:
<form id="formid" method="post" action="/">
rather than:
<form id="formid" method="post" action="/Subdomain3Controller">
when viewing:
http://localhost/Subdomain3Controller/ or http://localhost/Subdomain3Controller/Index
Stumbled on a similar problem and managed to find a solution for this problem. I'm aware this might not help the original author, but it might help some other people who end up here via search.
Anyway, it seems the route name "Default" doesn't add much weight. When BeginForm is constructing its URL it takes the first route from the route collection that 'fits'. I didn't dig too deep into how this all works internally.
What I did to solve this problem was add a constraint to the subdomain route so it can only be used for the Subdomain3Controller, as such:
var domainRoute = new DomainRoute(
"subdomain3.organisation.com", // Domain with parameters
"{action}/{id}", // URL with parameters
new { controller = "Subdomain3Controller", action = "Index", id = "" }
);
domainRoute.Constraints = new RouteValueDictionary();
webserviceRoute.Constraints.Add("Controller", "Subdomain3Controller");
routes.Add("DomainRoute", domainRoute);
[edit]
After thinking about this a little bit more and realizing my situation was a bit different from the situation of the original poster. The above won't work, because the controller is not inside of the URL anymore.
A solution might be to switch the order of the routes, and add a NotEqual constraint for the subdomains to the default route. As described in http://stephenwalther.com/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx
[/edit]

Categories

Resources