RESTful ASP.NET MVC3 Routing - asp.net

I've looked through the various questions already asked on this topic, and I've spent time trying to get it working how I would like it to, but I haven't had much luck so hopefully someone here can help me fill in the gaps.
With a new site I'm creating I wanted to try getting the URL structure to be more RESTful (I wanted to do it with my first MVC3 creations, but, time did not permit such experimenting). However, I don't want the different URLs to all point to the same action. I want different actions for each resource requested to keep the controller code concise and intuitive.
Here is an example of the URL structure I'm shooting for:
/Case //This currently works
/Case/123 //This currently works
/Case/123/Comment //This one does not work (404)
Here is how I currently have my routes setup:
routes.MapRoute(
"Case",
"Case/{id}",
new { controller = "Case", action = "Number" }
);
routes.MapRoute(
"CaseComment",
"Case/{caseId}/Comment/{id}",
new { controller = "Case", action = "CaseComment" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
The first two URL's I listed are worked correctly with this route structure. The first URL takes me to my listing page. When an id is specified, I hit the Number action so I can show details for that particular record. The Comment URL is not working.
I have the action for the third URL defined as:
public ActionResult CaseComment(string caseId, string id) {
//Narr
}
What am I missing? And, could I set this up in an easier fashion for future resources?

I believe MapRoutes are order specfic, so
/Case/123/Comment
is using your
routes.MapRoute(
"Case",
"Case/{id}",
new { controller = "Case", action = "Number" });
route, thus throwing a 404. Most specific route should be place above more general routes.

Try placing the CaseComment route above the Case route.

Mapping routes are order specific.
One thing you may wish to consider for restful routing in MVC is a project called RestfulRouting that is on GitHub originally written by Steve Hodgekiss.
https://github.com/stevehodgkiss/restful-routing
If nothing else, looking at the code may well help you.
Hope this helps.

I needed to make the id parameter optional.
routes.MapRoute(
"Case Resources",
"Case/{caseId}/{action}/{id}",
new { controller = "Case", action = "CaseComment", id = UrlParameter.Optional }
);

Related

In ASP.NET MVC5, how can I hide the Action name when an a tag is generated in Razor using Url.Action?

As the title says.
I have a route set up and working fine, which provides a default action when none is specified. I just want to hide the action from the URL because it's unnecessary clutter.
Setting the "ActionName" parameter as null, or "", will just result in the current page's action being substituted instead - which doesn't work.
I'm open to using #Html.ActionLink() if that will get me what I need.
My route definition is
routes.MapRoute(
name: "MyBookRoute",
url: "Book/{id}",
defaults: new { controller = "Book", action = "Index" }
);
If all else fails, I suppose I can deal with writing out the hrefs manually, but this should not be a difficult thing for Razor to do.
Has anyone else come across this and knows what to do?
Base on your route definition, then either
#Url.Action("Index", "Book", new { id = 1 })
or
#Html.ActionLink("Your link text", "Index", "Book", new { id = 1 }, null)
will remove the action name from the generated url.

Wrong URI after Form Submit MVC 3 asp.NET

I'm a little confused on something here. I have a form, and the URL for that form is:
http://domain.com/Home/requestquote
When I click submit on a form and the method handling this form located in the form controller sends back a view, the URI looks like this:
http://domain.com/form/requestQuoteSubmit where requestQuoteSubmit is the method.
This occurs both when validation sends the page back for errors and when there is a successful form submit.
On success the view being sent back is Home/thanks and on error it should just send back Home/requestquote. Everything seems to work fine except for the fact that the URI is not what it is supposed to be. This causes everything else on the page to break because my links look like this:
#Url.RouteUrl(Model.CompanyPageDatabaseModel.companyCode, new { Controller = MVC.services.Name, Action = MVC.services.ActionNames.page })
So that companyCode value isn't being passed around properly and forming the links correctly. I'm not sure how or why the form method is sending back the correct page, but setting the URI to itself?
Here are my routes.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
null,
"{action}",
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index },
new { RootAction = new RootActionConstraint() } // Route Constraint
);
routes.MapRoute(
null, // Route name
"{controller}/{action}", // URL with parameters
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index }, // Parameter defaults
new { controller = "Home|contact|franchise|form|resources|services|technology|community|careers|carriers|about" }
);
routes.MapRoute(
null,
"{companyCode}/{action}",
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index },
new { RootAction = new RootActionConstraint() } // Route Constraint
);
routes.MapRoute(
"jax",
"{companyCode}/{controller}/{action}",
new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Index }
);
The last route value is an example of a franchisee route.
Also here is the form code in the view:
#using (Html.BeginForm("requestquote", "form", FormMethod.Post))
{ }
Any ideas?
Thanks.
Edit: Added in my routes.
There is a concept referred to as PRG, which stands for Post-Redirect-Get.
The basic idea is that you Post the form to your application. After processing the input, you then Redirect to a Get request at the correct URL, instead of serving the user a content response directly from the submission.
This is good for many reasons, but the underlying one is separation of concerns. (When you don't separate concerns weird stuff like all the links might breaking on your page tends to happen...)
So, the solution for you is to process the submission, and if invalid, store your Validation content in TempData and return a RedirectToRouteResult(controller:"Home",action:"RequestQuote")
Update: I found the original article I read to learn this concept. It's in terms of the original ASP.NET MVC release, but it should be mostly the same. Check out http://www.eworldui.net/blog/post/2008/05/ASPNET-MVC---Using-Post2c-Redirect2c-Get-Pattern.aspx

mvc3 map routes

I am looking to use the following urls and maps some routes accordingly
<domain>/Home/About
<domain>/Home/SiteList
<domain>/Site/<id>/ (this one is defaulted to the details view)
<domain>/Site/<id>/section1/ (this one goes to section1 route in the Site controller)
<domain>/Site/<id>/section2/ (this one goes to section2 route in the Site controller)
e.g.
<domain>/Site/london/siteinfo
The above are covered by
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
However, i also want to use the following routes
<domain>/Site/<siteid>/section1/controller/action (where controller gets data linked to the <siteid> and action is optional)
an example link would be:
<domain>/Site/london/siteinfo/manager (manager is a controller and will list managers for the site)
if have tried various routes and read various posts but could not find anything which was applicable to me.
Can anyone provide some help please?
Thanks
Rudy
Add one more route below "Default":
routes.MapRoute(
"MyRouteName", // Route name
"{controller}/{action}/{id}/{section}/{subsection}", // URL with parameters
new { controller = "Home", action = "Index", id= "", section= "", subsection = "" } // Parameter defaults
);

ASP.Net MVC 2 Area, SubArea and Routes

I have been looking around for a solution for my problem. Found alot of similar issues, but none of them led to a solution for me.
I am trying to register an Area within an Area. This works however it "partially" screws up my routing.
My route registrations in the order they are registered, consider the FooBar and Foo registrations to be coming from AreaRegistrations
routes.MapRoute("FooBar_default",
"Foo/Bar/{controller}/{action}",
new { area = "Foo/Bar", controller = "Home", action = "Index"},
new[] { BarHomeControllerType.Namespace }
);
routes.MapRoute("Foo_default",
"Foo/{controller}/{action}/{id}",
new { area = "Foo", controller = "Start", action = "Index", id = UrlParameter.Optional },
new { controller = new NotSubArea()},
new[] { typeof(StartController).Namespace }
);
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("PagesRoute", "Pages/{action}", new { controller = "Pages", Action "Index" }).DataTokens["UseNamespaceFallback"] = false;
routes.MapRoute("Default", // Route name
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] { typeof(HomeController).Namespace }
).DataTokens["UseNamespaceFallback"] = false;
Now the following problem occurs. When going to Website/Foo/ or Website/Foo/Bar links in those pages are generated correctly using:
!{Html.ActionLink<HomeController>(c => c.Index(),"Home", new { area = "Foo/Bar"})}
or
!{ Url.Action("Index", "Home", new { area = "Foo/Bar"}) } //or a different area
However when i use this in my main pages, in other words Website/ or Website/Home etc..
!{Html.ActionLink<HomeController>(c => c.Index(),"Home", new { area = ""})}
or
!{ Url.Action("Index", "Home", new { area = ""}) }
//or with no area identifier specified
It generates the Url: Website/Foo/Bar/Home etc... Which ofcourse is wrong.
When i remove the Area registration for Foo/Bar it all works again. Going to the urls Website/Home/About or Website/Home directly does display the right pages, so im guessing somehow the internal UrlHelper is picking the wrong routes to render.
I tried switching the order of the FooBar_default and Foo_Default routes, so that the Foo_default route is registered before the FooBar_default route, but then the area does not work anymore (resource not found) and the links are still generated incorrectly.
What i find most odd is that removing the Foo/Bar registration solves the problem. I was hoping someone could shed some insight on this matter..
What you need to understand that an Area is just a routing concept which Microsoft have neatly wrapped up the concept or UrlRouting to get people started.
You can actually get the MVC framework to route your request however you like according to your requirements.
What you might need to look at doing, is writing your own RouteHandler. This will enable you to correctly direct how the MVC framework routes any request accoring to your requirements.
See this answer to asp.net mvc complex routing for tree path as an example to get you started.
chris166 outlines that my implementing your own IRouteHandler, and mapping your route to use that instead should get you what you need. Its a bit more effort than using out the box solution of areas, but should get you better results.
routes.MapRoute(
"Tree",
"Tree/{*path}",
new { controller = "Tree", action = "Index" })
.RouteHandler = new TreeRouteHandler();

ASP MVC Routing with > 1 parameter

I have the following route defined
routes.MapRoute(
"ItemName",
"{controller}/{action}/{projectName}/{name}",
new { controller = "Home", action = "Index", name = "", projectName = "" }
);
This route actually works, so if I type in the browser
/Milestone/Edit/Co-Driver/Feature complete
It correctly goes to the Milestone controller, the edit action and passes the values.
However, if I try and construct the link in the view with a url.action -
<%=Url.Action("Edit", "Milestone", new {name=m.name, projectName=m.Project.title})%>
I get the following url
Milestone/Edit?name=Feature complete&projectName=Co-Driver
It still works, but isn't very clean. Any ideas?
When constructing and matching routes in ASP.NET routing (which is what ASP.NET MVC uses), the first appropriate match is used, not the greediest, and order is important.
So if you have two routes:
"{controller}/{action}/{id}"
"{controller}/{action}/{projectName}/{name}"
in that given order, then the first one will be used. The extra values, in this case projectName and name, become query parameters.
In fact, since you've provided default values for {projectName} and {name}, it's fully in conflict with the default route. Here are your choices:
Remove the default route. Do this if you don't need the default route any more.
Move the longer route first, and make it more explicit so that it doesn't match the default route, such as:
routes.MapRoute(
"ItemName",
"Home/{action}/{projectName}/{name}",
new { controller = "Home", action = "Index", name = "", projectName = "" }
);
This way, any routes with controller == Home will match the first route, and any routes with controller != Home will match the second route.
Use RouteLinks instead of ActionLinks, specifically naming which route you want so that it makes the correct link without ambiguity.
Just to clear up, here is what I finally did to solve it, thanks to the answer from #Brad
<%=Html.RouteLink("Edit", "ItemName", new { projectName=m.Project.title, name=m.name, controller="Milestone", action="Edit"})%>
You can try
Html.RouteLink("Edit","ItemName", new {name=m.name, projectName=m.Project.title});

Resources