asp.net mvc routing question - asp.net-mvc

the default routing works fine
mysite.com/home/about
and i even see how to customize it to make it shorter
so i can say:
mysite.com/edit/1
instead of
mysite.com/home/edit/1
but how can i make it longer to handle url like the following
mysite.com/admin/user/1 // works
mysite.com/admin/user/details // does not work
mysite.com/admin/question/create // does not work
i cant just treat the id as an action? i need a custom route?
do i need to create new controllers for each table or can i route them all through the Admin controller
thanks a lot

As has been mentioned already, probably your best bet would be to use the new Areas feature
You can achieve this type of routing without Areas, but as the number of controllers gets large the maintainability of your site will diminish. Essentially what you do is to hard-code the controller name into the Route definition which means that you have to add new route mappings for each new Admin controller. Here's a few examples of how you might want to set up your routes without Areas.
routes.MapRoute("AdminQuestions", // Route name
"admin/question/{action}/{id}", // URL with parameters
new { controller = "AdminQuestion", action = "Index" } // Parameter defaults
);
routes.MapRoute("AdminUsers", // Route name
"admin/user/{action}/{id}", // URL with parameters
new { controller = "AdminUser", action = "Index" } // Parameter defaults
);
Alternatively you could route everything through the Admin controller, but it would quickly become very messy with your controller actions performing multiple roles.
routes.MapRoute("Admin", // Route name
"admin/{action}/{type}/{id}", // URL with parameters
new { controller = "Admin", action = "Index" } // Parameter defaults
);
With your AdminController action(s) looking like:
public virtual ActionResult Create(string type, int id)
{
switch (type)
{
case 'question':
// switch/case is code smell
break;
case 'user':
// switch/case is code smell
break;
// etc
}
}

Adding routes to global.asax is fairly straight forward. Put the more specific routes above the more general routes. The most typical pattern is controller/action/parameter/parameter... If you need something more complex, you may want to look at MVC Areas.In you example above "mysite.com/admin/user/details" is looking for a controller named "admin" and an action named "user", with everything after that being parameter on the action method (assuming a typical route setup)

Related

Conditional routing in ASP.NET MVC

I'll try to describe my problem as simple as I can. I have several controllers which are loaded dynamically at runtime. Those controllers hosts several web apis for different partners. What I want to achieve is to have a prefix in URL before accessing the controller. In other words, I have Partner1 and Partner2. Both of them has some controllers, for example
Partner1: Service1Controller, Service2Controller2.
Partner2: Api1Controller, Api2Controller etc.
Now I want to achieve the following. I want partner1's controllers to be accessible only with Partner1 prefix in the url, for example http://somehost.com/Partner1/Service1, but I don't want Api1Controller to be accessible from http://somehost.com/Partner1/Api1, but instead it should be accessible from http://somehost.com/Partner2/Api1.
Is there any way to achieve my goal?
Thanks
An inelegant solution could be to use 2 separate route mappings:
routes.MapRoute(
"partner1",
"partner1/{controller}/{action}",
new { contoller = Service1Controller, action = "Index" },
new { contoller = #"(Service1Controller)|(Service2Controller)"}
);
routes.MapRoute(
"partner2",
"partner2/{controller}/{action}",
new { controller = Api1Controller, action = "Index" },
new { contoller = #"(Api1Controller)|(Api2Controller)"}
);
Teh 4th parameter of MapRoute defines constraints as regular expressions
You could create a custom ActionFilter that looks at the Request and User and determines if the route is valid. If it's not, then you can return some ActionResult that either redirects or returns an error to the user.

How can I achieve clean URL routing with custom user ID?

My ASP.NET MVC site allows users to register and give themselves user names, which will be unique and allow others to browse their pages with a clean URL that includes their name, like Twitter, Facebook, LinkedIn etc. do.
For example:
mysite.com/michael.guthrie
mysite.com/john
mysite.com/john/images
mysite.com/john/blog
etc.
The problem is that the first URL segment might be used for other "regular" controllers/actions, like:
mysite.com/about
mysite.com/register
So basically I seek for a routing scheme that says something like: If the first URL segment is a known controller, treat it as a controller (and parse the relevant action and parameters as usual), but if not - treat it as a user name, and pass it to a dedicated controller+action which will parse it and continue accordingly.
I don't want a solution that will enforce me to add routes for every specific controller that I have, such that after the routing module will go over all of them and won't find a match, it will get to the last one which defines a route for this special user name segment. The reason is primarily maintenance (I must remember to add a route every time I code a new controller, for example.)
I assume I can implement my own MvcRouteHandler / IRouteHandler but I feel there must be simpler solution that won't have me tweak MVC's out-of-the-box routing mechanism.
Note: I've read How to achieve nice litle USER page url like facebook or twitter? and it doesn't answer my question, it's just says that there is a URL rewriting module.
Do you know any good, elegant, clean way to achieve that?
You should have your first route be your Usesr route, with a route constraint along the lines of what I described in this answer: MVC routing question.
If your route is in the form {username}/{controller}/{id}, this route should cover all contingencies.
in the global.asax file you can map your routes
in the registerRoutes() method you can do something like this:
routes.MapRoute(
"ToonStudenten", // Route name
"{controller}/{action}/{userID}, // URL with parameters
new { controller = "Docent", action = "ToonStudenten", userID = UrlParameter.Optional} // Parameter defaults
);
I believe you can change the way your views look with this mapRouting, not entirely sure how though.. will try and search it up
You may want to take a look at this post:
MVC 3 keeping short url
You don't need to set a route for each URL. With a little help from route constraints you can do something like this:
routes.MapRoute(
"Home", // Route name
"{action}", // URL with parameters
new { controller = "Home", action = "Index" }, // Parameter defaults
new { action = "TaskA|TaskB|TaskC|etc" } //Route constraints
);
routes.MapRoute(
"Account", // Route name
"{action}", // URL with parameters
new { controller = "Account", action = "Logon" }, // Parameter defaults
new { action = "Logon|Logoff|Profile|FAQs|etc" } //Route constraints
);

How can i create a route that returns a URL without controller reference in ASP.NET MVC

I have a controller call DefaultController. Inside this controller i have views for what would be the equivalent of static pages.
The URLs look like www.site.com/Default/PageName
Is it possible to create a route that would format these URL like:
www.site.com/PageName
I want to avoid creating controllers for each of these. An alternative would be to create .aspx pages in the root but can i create routes for these pages ie:
www.site.com/PageName.aspx becomes www.site.com/PageName ?
Thanks!
You can create explicit route for the PageName action on the DefaultController like this:
routes.MapRoute(
"PageName",
"pagename",
new { controller = "DefaultController", action = "PageName" }
);
You have to put this route before the default MVC route. The biggest drawback for this approach is that you have to create one route per static page.
An alternative approach would be to add an additional route after the default MVC route:
routes.MapRoute(
"DefaultController",
"{page}/{*path}",
new { controller = "DefaultController", action = "{page}" }
);
The drawback for this approach is that this rule would be handling all the URLs, even those that would normally return 404.
First approach
Create a route that catches actions:
routes.MapRoute(
"Catcher1",
"{action}",
new { controller = "Default", action = string.Empty });
But this means you'd have to create just as many controller actions on your default controller.
Second approach
If you'd like to avoid that as well and have just one controller+action instead, write a route this way:
routes.MapRoute(
"Catcher2",
"{path}",
new { controller = "Default", action = "PageName", path = string.Emtpy },
new { path = #"[a-zA-Z0-9]+" });
This route also defines a route constraint so it will catch only those routes, that actually have something in first route segment. You can define this constraint to only catch those requests that you need (ie. path = "Result|Search|Whatever")
then your DefaultController would have something like this:
public ActionResult PageName(string path)
{
// code goes here
}
Second approach seems very feasible, but I wouldn't recommend it because all logic would have to go through this controller action (for these kind of requests). It would be better to separate these actions into logical ones. Those that actually do the same thing (so they wouldn't have a bunch of switch statements or similar) would be defined with separate routes (if they couldn't be done using a single one).

Areas And Routes

I'm using areas everywhere and I'm wanting something like the following:
http://localhost/MyArea/MySection/MySubSection/Delete/20
Usually I access things by doing the following:
http://localhost/MyArea/MySection/MySubSection/20
But if I want to delete then I have to say
http://localhost/MyArea/MySection/DeleteEntryFromMySubSection/20
With routes, how do you do this? (the routes aren't realistic by the way, they're much more concise than this in my system)
EDIT: This is specifically related to the use of Areas, an ASP.NET MVC 2 Preview 2 feature.
It would depend on how your routes & controllers are currently structured.
Here's an example route you might want to use.
If you want to be able to call the following route to delete:
http://localhost/MyArea/MySection/MySubSection/Delete/20
And let's assume you have a controller called "MyAreaController", with an action of "Delete", and for the sake of simplicity let's assume section and subsection are just strings e.g.:
public class MyAreaController : Controller
{
public ActionResult Delete(string section, string subsection, long id)
{
Then you could create a route in the following way (in your Global.asax.cs, or wherever you define your routes):
var defaultParameters = new {controller = "Home", action = "Index", id = ""};
routes.MapRoute("DeleteEntryFromMySubSection", // Route name - but you may want to change this if it's used for edit etc.
"{controller}/{section}/{subsection}/{action}/{id}", // URL with parameters
defaultParameters // Parameter defaults
);
Note: I'd normally define enums for all the possible parameter values. Then the params can be of the appropriate enum type, and you can still use strings in your path. E.g. You could have a "Section" enum that has a "MySection" value.

How should i make this ASP.NET MVC Route?

i wish to have the following url(s).. and i'm not sure how i should do the following:
1) Route registered in the global.asax
2) Controller method
Urls/Routes
- http://www.mysite.com/
- http://www.mysite.com/?page=2
- http://www.mysite.com/?tags=fooBar
- http://www.mysite.com/?page=2&tags=fooBar
Please note - i do not want to have http://www.mysite.com/{page}/{tags}/ etc.. if that difference makes sence. I also understand about the default routes, but i'm not sure how to tweak them to make it do what i require.
Lastly, i also know how to use Html.ActionLink(..) so I'm not worried about how to use that.
any suggestions?
Unit Testing
I'm also under the impression that i could do a unit test, like the following:-
(using MvcFakes)...
// Arrange.
var routes = new RouteCollection();
MvcApplication.RegisterRoutes(routes);
// Act.
context = new FakeHttpContext("~/?page=2&tags=fooBar");
routeData = routes.GetRouteData(context);
// Assert.
Assert.AreEqual("Home", routeData.Values["controller"]);
Assert.AreEqual("Index", routeData.Values["action"]);
Assert.AreEqual(2, routeData.Values["page"]);
Assert.AreEqual("fooBar", routeData.Values["tags"]);
Update 1
I'm hoping to run all these of the Index action on the default HomeController, if this helps. (in actual fact, I've renamed my HomeController to PostController but that is not really important / shouldn't effect the problem).
Actually for what you are trying to do you don't need additional route. The default MVC route handles your request well. You just have to keep in mind that controller action parameter names must match your url param names.
URL: http://www.mysite.com/?page=2&tags=fooBar
public ActionResult Index(string page, string tags)
{
ViewData["Message"] = string.Format("Page={0}, Tags={1}", page, tags);
return View();
}
Of course thats for Controller "Home" and Action "Index" as defaults. But the point is clear I hope.
Scott Guthrie has excellent post about routing Here

Resources