ASP.NET-MVC . How to get the controller name from an url? - asp.net-mvc

How can I get the controller name of a relative Url, using the routes I have defined in Global.asax?
Example:
if I have a route defiend like this:
routes.MapRoute(
"Default", // Route name
"{language}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "", language = "en" }
from the string "~/en/products/list" I want to have products (the controller name). Is there any existing method that already does this?

You should probably add another route like George suggests but if you really just need the controller value derived from the route you can do this in your controller action methods:
var controller = (string)RouteData.Values["controller"];

See Stephen Walther's blog post ASP.NET MVC Tip #13 – Unit Test Your Custom Routes
The project MvcFakes has an old System.Web.Abstractions reference. So you must replace it
with the new one and recomply the project to get MvcFakes.dll.
This is my code:
public string getControllerNameFromUrl()
{
RouteCollection rc = new RouteCollection();
MvcApplication.RegisterRoutes(rc);
System.Web.Routing.RouteData rd = new RouteData();
var context = new FakeHttpContext("\\" + HttpContext.Request.Url.AbsolutePath);
rd = rc.GetRouteData(context);
return rd.Values["action"].ToString();
}
In my code above "MvcApplication" is the class name in the Global.asax.
Good luck !

I'm not sure what you're asking, so if my answer's wrong, it's because I'm guessing at what you want.
You can always add another route to your Global.asax. That's often the easiest way to deal with cases 'outside of the norm'.
If you want to return a list of products, you'll use this route:
routes.MapRoute(
"ProductList",
"{language}/{products}/{action}/",
new { controller = "Products", action = "List", language = "en" });
You can also replace products with the more generic {controller} if more than one type of entity is going to use this route. You should modify it for your needs.
For example, to make this a generic route that you can use to get a list of any product:
routes.MapRoute(
"ProductList",
"{language}/{controller}/{action}/",
new { controller = "Products", action = "List", language = "en" });
What this does is that it creates a route (that you should always place before your Default route) that says, "For whatever the user enters, give me the controller and action they ask for". (Such as /en/Products/List, or /en/Users/List).
To visit that controller, you simply need to navigate to the following: yoursite.com/en/products/list. You can also use the HTMLActionLink to visit the controller.
<%=Html.ActionLink("Product", "List", new { controller = Products }, null ) %>
I'm writing this without my IDE open, so the ActionLink may have an error in it.

Related

How do I use constraints in ASP.net MVC 4 RouteConfig.cs?

I'm trying to get some routing constraints working using the latest asp.net mvc 4 architecture. Under App_Start there is a file called RouteConfig.cs.
If I remove the constraints section from my example below, the url works. But I need to add some constraints so that the url doesnt match on everything.
Should work: /videos/rating/1
Shold NOT work: /videos/2458/Text-Goes-Here
This is what I have:
//URL: /videos/rating/1
routes.MapRoute(
name: "Videos",
url: "videos/{Sort}/{Page}",
defaults: new { controller = "VideoList", action = "Index", Sort = UrlParameter.Optional, Page = UrlParameter.Optional },
constraints: new { Sort = #"[a-zA-Z]", Page = #"\d+"}
);
If you want multiple optional parameters on the same route, you will run into trouble because your urls must always specify the first one in order to use the second one. Just because you use constraints doesn't stop it from evaluating the parameters, it instead fails to match this route.
Take this for example: /videos/3
When this is trying to match, it finds videos, and says, "OK, I still match". Then it looks at the next parameter, which is Sort and it gets the value 3, then checks it against the constraint. The constraint fails, and so it says "OPPS, I don't match this route", and it moves on to the next route. In order to specify the page without the sort parameter defined, you should instead define 2 routes.
//URL: /videos/rating/1
routes.MapRoute(
name: "Videos",
url: "videos/{Sort}/{Page}",
defaults: new { controller = "VideoList", action = "Index", Page = UrlParameter.Optional },
constraints: new { Sort = #"[a-zA-Z]+", Page = #"\d+"}
);
//URL: /videos/1
routes.MapRoute(
name: "Videos",
url: "videos/{Page}",
defaults: new { controller = "VideoList", action = "Index", Sort = "the actual default sort value", Page = UrlParameter.Optional },
constraints: new { Page = #"\d+"}
);
I put the most specific routes first when possible and end with the least specific, but in this case the order should not matter because of the constraints. What I mean by specific is most defined values, so in this case you must define the sort in the first route, and you also can specify the page, so it is more specific than the route with just the page parameter.
My input maybe rather late, but for others still searching for answers.To keep things simple, i would use the following in my RoutesConfig file
routes.MapRoute(
name: "Videos",
url: "{controller}/{action}/{id}",
defaults: new { controller = "VideoList", action = "Index", id="" },
constraints: new { id = #"\d+"}
);
Depending on your choice of implementation, id could be UriParameter.Optional, but in this scenario it is going to be id="" ,because we will be passing a string/int during runtime.
This style was adopted from http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/creating-a-route-constraint-cs
One thing to keep in mind by convention controller classes always end with controller e.g VideoListController class. This class should be listed under the controller folder containing the following method
public ActionResult Index(string id)
{
// note this maps to the action
// random implementation
ViewBag.Message=id;
View()
}
// note this approach still matches any string...
To match only integers, the Index method has to be rewritten
public ActionResult Index(int id)
{
// note this maps to the action
ViewBag.Message=id;
View()
}
Consequently, this approach works for VideoList/Index/12
but upon putting VideoList/Index/somerandomtext it throws an error during runtime. This could be solved by employing error pages.
I hope this helps. Vote if its quite useful.

How do I do Short URLs in MVC?

Suppose I want to publish (like in paper catalogs) some "short URLs" that are easy to type/remember, but I want them to redirect to a verbose, SEO-friendly URL. How do I accomplish that with MVC routes?
Example:
http://mysite.com/disney
becomes
http://mysite.com/travel/planning-your-disney-vacation (with "travel" as the Controller)
The things I've tried:
Just setup a route for it. Problem: the URL doesn't change in the browser (it stays "/disney".
Use NuGet package RouteMagic (see Haacked's article). Problem: I get an error: The RouteData must contain an item named 'controller' with a non-empty string value. I think this is because I don't have a static word before my controller ("travel") like he did (with "foo" and "bar")???
Use a redirect module (like Ian Mercer's). Problem: the route matches on my HTML.ActionLinks when creating URLs which I don't want (Haacked mentions this in his article and says that's why he has GetVirtualPath return NULL ...?)
I'm out of ideas, so any would be appreciated!
Thanks!
You could set up a catch-all type route, to direct all /something requests to a specific action and controller, something like:
routes.MapRoute(
"ShortUrls",
"{name}",
new {controller = "ShortUrl", action = "Index", name = UrlParameter.Optional}
);
(depending on how the rest of your routing is set up, you probably don't want to do it exactly like this as it will likely cause you some serious routing headaches - but this works here for the sake of simplicity)
Then just have your action redirect to the desired URL, based on the specified value:
public class ShortUrlController : Controller
{
//
// GET: /ShortUrl/
public ActionResult Index(string name)
{
var urls = new Dictionary<string, string>();
urls.Add("disney", "http://mysite.com/travel/planning-your-disney-vacation");
urls.Add("scuba", "http://mysite.com/travel/planning-your-scuba-vacation");
return Redirect(urls[name]);
}
}
I just faced the same problem.
In my Global:
routes.MapRoute(
"ShortUrls",
"{name}",
new { controller = "Home", action = "Index", name = UrlParameter.Optional }
);
In my Home Controller:
public ActionResult Index(string name)
{
return View(name);
}
This way is dynamic, didn't want to have to recompile every time I needed to add a new page.
To shorten a URL you should use URL rewriting technique.
Some tutorials on subject:
url-rewriting-with-urlrewriternet
url-routing-with-asp-net-4
URL rewriting in .Net

How can I dynamically add to the ASP.NET MVC RouteTable?

We've got an area on our site where people can sign up and be given their own page on the site which we want to host at ~/pageSlug. I've tried doing it with a rule in Global.asax, but that broke the fundamental default route that allows ~/Controller to map directly to the Index action. I'm not being allowed to put any kind of separator in front of the userSlug, so ~/p/pageSlug isn't really an option here.
In terms of getting the user pages added to the routes, I'm cycling through the pages at App_Start and explicitly adding them to the RoutesTable. This is working fine, and we've got AppPool refreshes set long enough to make this a once a day task. This does leave us with a 24-hour turnaround to "get pages live" for our users though, which I'm trying to solve.
Ideally, what I'd like to do is add the relevant route to the RouteTable dynamically once a user has signed up. I've tried doing that with:
RouteTable.Routes.Add(
RouteTable.Routes.MapRoute(
toAdd.UrlSlug + "Homepage",
toAdd.UrlSlug,
new { controller = "Controller", View = "View", urlSlug = toAdd.UrlSlug }
)
);
but that didn't seem to work. I can't find a solution anywhere else, and I'm sure my code is both horribly naive and betrays a lack of understanding of routing - please help!
What if you try this, using route constraint. Get out list of all users and constraint the chosen route to match entries in that list
public class UserPageConstraint : IRouteConstraint
{
public static IList<string> UserPageNames = (Container.ResolveShared<IUserService>()).GetUserPageNames();
bool _IsUserPage;
public UserPageConstraint(bool IsUserPage)
{
_IsUserPage = IsUserPage;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (_IsUserPage)
return UserPageNames.Contains(values[parameterName].ToString().ToLower());
else
return !UserPageNames.Contains(values[parameterName].ToString().ToLower());
}
}
Then in the Global.asax.cs, set up a route for users as follows:
routes.MapRoute("UserHome", "{userPage}", new { controller = "UserPageController", action = "Index" }, new { userPage = new UserPageConstraint(true) });
For this above route, in the action 'index' of the UserPageController, we will have the userPage as the parameter.
For other routes relative to the userPage Home, we can add routes accordingly. for example, the userdetails page route can be added as follows:
routes.MapRoute("UserHome", "{userPage}/mydetails", new { controller = "UserPageController", action = "Details" }, new { userPage = new UserPageConstraint(true) });
You can try this and see.
I recently just realized it too, instead of adding routes dynamically which I am not sure can be done so without using external libraries such as Route Magic
. I am confident that if you design your architecture right, Routing Constraint is all you might just need. Since my application is small, I am using only one implemented controller and dynamically creating the rest (experimental at the moment but it should work).
Consider following routes
routes.MapRoute(
name: "ContactRequests_Operations",
url: "ContactRequests-{operation}",
defaults: new { controller = "Content", action = "Module_Direct", id = "ContactRequests" , operation = "Get" }
);
routes.MapRoute(
name: "Messages",
url: "Messages",
defaults: new { controller = "Content", action = "Direct", id ="Messages" }
);
I don't think you can add route dynamically.
Take a look at this question, maybe it will help you:
ASP.NET MVC custom routing for search

Vanity MVC Routes?

I want to have a route that looks something like: www.abc.com/companyName/Controller/Action/Id
However, all the company names need to map to the same "base" controllers, regardles of what the name is. I only need the companyName for authentication purposes.
Also, if there's no companyName provided, I need to map to a different set of controllers altogether.
How do I do this? I'd also appreciate a good routing resource so I don't have to ask questions like this.
routes.MapRoute(
"CompanyRoute",
"{companyName}/{controller}/{action}/{id}",
new { controller = "MyBaseCompanyController", action = "Index", id = "" }
);
routes.MapRoute(
"NoCompanyRoute",
"{controller}/{action}/{id}",
new {controller = "DifferentDefaultController", action = "Index", id = "" });
Routing is quite a complex topic, but it's covered well in Professional ASP.Net MVC 1.0. For online resources, I would suggest starting here, and then coming back to Stack Overflow ;)
In case if you wish to Resolve the errors caused due to routing . i suggest the following tool , which i found to be extremely useful.
Route Debugger
Go to Global.asax.cs, and add the following route in the RegisterRoutes() method before the "Default" route:
routes.MapRoute(
"Vanity", // Route name
"{company}/{controller}/{action}/{id}", // URL with parameters
new { company = "", controller = "Home", action = "Index", id = "" } // Parameter defaults
);

ASP.Net MVC routing action name conflicts

We have a website that deals with artists and venues and we're developing it in ASP.net MVC.
We have our artist views in a folder (Views/Artists/..), an ArtistsController, ArtistsRepository and adhere to the REST action names such as Show, New, Delete etc.
When we first mocked up the site, everything worked well in our test environment as our test URLs were /artists/Show/1209
but we need to change this so the website appears as /artists/Madonna and /artists/Foo-Fighters etc
However, how can we distinguish between valid artist names and the names of the actions for that controller?! For example, artists/PostComment or artists/DeleteComment? I need to allow the routing to handle this. Our default Show route is:
routes.MapRoute(
"ArtistDefault",
"artists/{artistName}",
new { controller = "Artists", action = "Show", artistName = ""}
One way around this is for our website to visibly run on /artists, but have our controller renamed to the singular - ArtistController - as opposed to ArtistsController. That would go against the naming conventions we went with when we started (but hey!).
Do you have any other recommendations? If possible we could also route depending on the verbs (so PostComment would be a POST so we could perhaps route to that action), but I'm not sure if that is advisable let alone possible.
Thanks
The 4th parameter to MapRoute allows you to specify restrictions for values. You can add a route before this one that is for "artists/{action}/{id}" with a restriction on the valid values for action; failing to match one of your actions, it'll fall through to the next route which will match for artist name.
You would actually define multiple routes... the defined actions in your controller would go first with the default being at the bottom. I like to think of route definitions as a "big 'ole switch statement" where first rule satisfied wins..
routes.MapRoute(
"ArtistPostComment",
"artists/PostComment/{id}",
new { controller = "Artists", action = "PostComment", id = "" }
);
routes.MapRoute(
"ArtistDeleteComment",
"artists/DeleteComment/{id}",
new { controller = "Artists", action = "DeleteComment", id = "" }
);
routes.MapRoute(
"ArtistDefault",
"artists/{artistName}",
new { controller = "Artists", action = "Show", artistName = "" }
);

Resources