ASP.NET MVC - Removing controller name from URL - asp.net-mvc

I want to remove the controller name from my URL (for one specific controller). For example:
http://mydomain.com/MyController/MyAction
I would want this URL to be changed to:
http://mydomain.com/MyAction
How would I go about doing this in MVC? I am using MVC2 if that helps me in anyway.

You should map new route in the global.asax (add it before the default one), for example:
routes.MapRoute("SpecificRoute", "{action}/{id}", new {controller = "MyController", action = "Index", id = UrlParameter.Optional});
// default route
routes.MapRoute("Default", "{controller}/{action}/{id}", new {controller = "Home", action = "Index", id = UrlParameter.Optional} );

To update this for 2016/17/18 - the best way to do this is to use Attribute Routing.
The problem with doing this in RouteConfig.cs is that the old route will also still work - so you'll have both
http://example.com/MyController/MyAction
AND
http://example.com/MyAction
Having multiple routes to the same page is bad for SEO - can cause path issues, and create zombie pages and errors throughout your app.
With attribute routing you avoid these problems and it's far easier to see what routes where. All you have to do is add this to RouteConfig.cs (probably at the top before other routes may match):
routes.MapMvcAttributeRoutes();
Then add the Route Attribute to each action with the route name, eg
[Route("MyAction")]
public ActionResult MyAction()
{
...
}

Here is the steps for remove controller name from HomeController
Step 1:
Create the route constraint.
public class RootRouteConstraint<T> : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var rootMethodNames = typeof(T).GetMethods().Select(x => x.Name.ToLower());
return rootMethodNames.Contains(values["action"].ToString().ToLower());
}
}
Step 2:
Add a new route mapping above your default mapping that uses the route constraint that we just created. The generic parameter should be the controller class you plan to use as your “Root” controller.
routes.MapRoute(
"Root",
"{action}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { isMethodInHomeController = new RootRouteConstraint<HomeController>() }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Now you should be able to access your home controller methods like so:
example.com/about,
example.com/contact
This will only affects HomeController. All other Controllers will have the default routing functionality.

If you want it to apply to all urls/actions in the controller (https://example.com/action), you could just set the controller Route to empty above ApiController. If this controller going to be your starting controller, you'll also want to remove every launchUrl line in launchSettings.json.
[Route("")]
[ApiController]

You'll have to modify the default routes for MVC. There is a detailed explanation at ScottGu's blog:
http://weblogs.asp.net/scottgu/archive/2007/12/03/asp-net-mvc-framework-part-2-url-routing.aspx
The method you should change is Application_Start. Something like the following might help:
RouteTable.Routes.Add(new Route(
Url="MyAction"
Defaults = { Controller = "MyController", action = "MyAction" },
RouteHandler = typeof(MvcRouteHandler)
}
The ordering of the routes is significant. It will stop on the first match. Thus the default one should be the last.

routes.MapRoute("SpecificRoute", "MyController/{action}/{id}",
new {controller = "MyController", action = "Index",
id = UrlParameter.Optional});
// default route
routes.MapRoute("Default", "{controller}/{action}/{id}",
new {controller = "Home", action = "Index",
id = UrlParameter.Optional} );

Related

How to map same routes for two controller?

I have two controller in my MVC application. One is Home controller and other is User controller. I am using following RouteConfig settings.
routes.MapRoute(
"actiononly",
"{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I want abc.com/Blog
abc.com/Login
Instead of abc.com/Home/Blog
abc.com/User/Login.
Above configuration works fine with abc.com/Blog but it is not working with abc.com/Login.
How to remove controller name from the link for both controllers?
Also how can I only show abc.com when website launches instead of abc.com/index? I am using following code in my webpage to access the particular page.
#Html.ActionLink("Home", "Blog", "Home")
#Html.ActionLink("Login", "Login", "User")
Your default route should automatically cater for wanting to nav to abc.com without requiring the index part of the URL
You need to ensure that your main route is specified as the default:
context.MapRoute(
"Site_Default",
"{controller}/{action}/{*id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you want to map short routes you can do exactly what you've done above. I use this helper function in my own project to map short routes:
void ShortRoute(string action, string controller)
{
ShortRoute(action, controller, action);
}
void ShortRoute(string action, string controller, string route)
{
_context.MapRoute(route, route, new { action, controller });
}
With usage:
ShortRoute("About", "Home");
Which allows me to navigate to mywebsite.com/about instead of mywebsite.com/home/about
If it's not working for certain URLs it may be that the route handler is matching a different route - I believe it does depend on the order you register them
There's a good route debugging plugin you can use
https://www.nuget.org/packages/routedebugger/
It gives you a summary of all routes and which ones matched the current URL - very useful
Without bringing in additional packages, you simply need to add an additional route. To create the new route, you first must define what you want your URL to be. In this case, you have said you want to be able to go to /Login which is on the user controller. Ok - let's create a new route. This route should be located ABOVE your default route.
routes.MapRoute(
"UserLogin",
"Login/{id}",
new { controller = "User", action="Login", id = UrlParameter.Optional }
);
The first parameter is simply the route name. The second parameter is the format of the URL that I want this route to match. Given we know what action we want to match to this route, we don't need the {action} or {controller} catchall placeholders that are in the default route. Also note that we can declare what controller this route will hit without having to specify the controller in the URL.
Last note, you don't have to have the {id} be part of the route if you will never be passing an ID parameter to that function. If that is the case, then you can safely remove any references to id in the UserLogin route.
As I re-read your question, you should be able to do this for some of your other examples as well. Let's take the /About URL and demonstrate the removal of the {id} parameter.
routes.MapRoute(
"AboutUsPage",
"About",
new { controller = "Home", action="About"}
);
This is very simple. You just need to create a route for each of your expected URLs.
Keep in mind that if you don't pass the controller or action as a URL placeholder, you will need to do so manually by providing them as default values.
routes.MapRoute(
"Blog",
"Blog/{id}",
new { controller = "Home", action = "Blog", id = UrlParameter.Optional }
);
routes.MapRoute(
"Login",
"Login/{id}",
new { controller = "User", action = "Login", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

MVC 5: Controller's action based routing

Is there a way to have different routing based upon controller's action?
For example:
Default routing
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
this would make the url look like
localhost:/Home/{someaction}/{id}
if the controllers action is
public ActionResult SomeAction(int id)
{
return Content("Sup?");
}
but lets suppose I have this action
public ActionResult AnotherAction(Guid productCategoryId, Guid productId)
{
return content("Hello!");
}
if I don't have any custom routing then the route would look like
localhost:/Home/AnotherAction?productCategoryId=someGuidId&productId=someGuidId
but for this action if I want the route to look like
localhost/Home/AnotherAction/productCategoryGuidId/productGuidId
how would I do that?
I have added a custom route
routes.MapRoute(
name: "appointment",
url: "{controller}/{action}/{appointmentId}/{attendeeId}",
defaults: new {controller = "Home",action = "Index", appointmentId = "",attendeeId="" }
);
but how do I say a controller's action to use that route and not default route.
Also, I read there is attribute routing in MVC 5. Would this help in my case? How would I use it in my case?
Register your custom MapRoute before your default Route. The order of which come first counts in the table route.
Routes are applied in the order in which they appear in the RouteCollection
object. The MapRoute method adds a route to the end of the collection, which means that routes are generally applied in the order in which we add them.
Hope It will help

Setting up Index as the default route for a controller

I have a url
http://www.roadkillwiki.org/Page/Index/documentation
which I want to turn into
http://www.roadkillwiki.org/Page/documentation
That could also be something like http://www.roadkillwiki.org/Page/my-url-with-spaces - the parameter is a string. The route setup I've tried is:
routes.MapRoute(
"ControllerDefault",
"{controller}/{id}",
new { controller = "Page", action = "Index", id = UrlParameter.Optional }
);
However this is interfering with the default "id" route that MVC projects come with. Is there any way of achieving this?
You don't need to lose the default route. The key to avoiding your routes interfere with each other is to order them so the more specific rules precede the less specific ones. For example:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{slug}",
new { controller = "Page", action = "Index" }
);
// Default MVC route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Then your PageController would look like this:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(string slug)
{
// find page by slug
}
}
That said, I would strongly advice you to do this instead:
// Your specialized route
routes.MapRoute(
"Page",
"Page/{id}/{slug}",
new { controller = "Page", action = "Index", slug = UrlParameter.Optional }
);
// MVC's default route (fallback)
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And your PageController:
using System.Web.Mvc;
public class PageController : Controller
{
public string Index(int id)
{
// find page by ID
}
}
By including the page ID either at the beginning of your URL (like StackOverflow does) or at the end, you can then just ignore the slug, and instead retrieve your pages by ID. This will save you a ton of headaches if your users change the page name. I have gone through this and it's painful; you basically have to keep a record of all names your pages have had in the past, just so your visitors/search engines don't get a 404 every time a page is renamed.
Hope this helps.
If you don't need a default route that came with project template you can set up one like this:
routes.MapRoute(
"ControllerDefault",
"{controller}/{pagename}",
new { controller = "Page", action = "Index" }
);
And than in your controller you would have an action:
public ActionResult Index(string pagename)
{
//do something
}

How do I configure ASP.NET MVC routing to hide the controller name on a "home" page?

Following on from this question:
ASP.NET MVC Routing with Default Controller
I have a similar requirement where my end user doesn't want to see the controller name in the url for the landing or "home page" for their application.
I have a controller called DeviceController which I want to be the "home page" controller. This controller has a number of actions and I'd like to use URL's like the following:
http://example.com -> calls Index()
http://example.com/showdevice/1234 -> calls ShowDevice(int id)
http://example.com/showhistory/1224 -> calls ShowHistory(int id)
I also need links generated for this controller to leave out the /device part of the url.
I also have a number of other controllers, for example BuildController:
http://example.com/build
http://example.com/build/status/1234
http://example.com/build/restart/1234
and so on. The URL's for these controllers are fine as they are.
The problem is that I just can't seem to get my head around the routing for this even after studying the answers to the question referenced above.
Can someone provide a code sample explaining how to do this?
I'm using ASP.NET MVC2.
Try this:
private void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("default", "{controller}/{action}/{id}",
new { action = "index", id = "" },
// Register below the name of all the other controllers
new { controller = #"^(account|support)$" });
routes.MapRoute("home", "{action}",
new { controller = "device", action = "index" });
}
e.g. /foo
If foo is not a controller then it's treated as an action of the device controller.
Step 1:
Create the route constraint.
public class RootRouteConstraint<T> : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var rootMethodNames = typeof(T).GetMethods().Select(x => x.Name.ToLower());
return rootMethodNames.Contains(values["action"].ToString().ToLower());
}
}
Step 2:
Add a new route mapping above your default mapping that uses the route constraint that we just created. The generic parameter should be the controller class you plan to use as your “Root” controller.
routes.MapRoute(
"Root",
"{action}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional},
new {isMethodInHomeController = new RootRouteConstraint<HomeController>()}
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new
{controller = "Home", action = "Index", id = UrlParameter.Optional}
);
Now you should be able to access your home controller methods like so:
example.com/about,
example.com/contact
This will only affects the url of HomeController. Alll other Controllers will have the default routing functionality.

How can I get the route name in controller in ASP.NET MVC?

ASP.NET MVC routes have names when mapped:
routes.MapRoute(
"Debug", // Route name -- how can I use this later????
"debug/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = string.Empty } );
Is there a way to get the route name, e.g. "Debug" in the above example? I'd like to access it in the controller's OnActionExecuting so that I can set up stuff in the ViewData when debugging, for example, by prefixing a URL with /debug/...
The route name is not stored in the route unfortunately. It is just used internally in MVC as a key in a collection. I think this is something you can still use when creating links with HtmlHelper.RouteLink for example (maybe somewhere else too, no idea).
Anyway, I needed that too and here is what I did:
public static class RouteCollectionExtensions
{
public static Route MapRouteWithName(this RouteCollection routes,
string name, string url, object defaults, object constraints)
{
Route route = routes.MapRoute(name, url, defaults, constraints);
route.DataTokens = new RouteValueDictionary();
route.DataTokens.Add("RouteName", name);
return route;
}
}
So I could register a route like this:
routes.MapRouteWithName(
"myRouteName",
"{controller}/{action}/{username}",
new { controller = "Home", action = "List" }
);
In my Controller action, I can access the route name with:
RouteData.DataTokens["RouteName"]
If using the standard MapRoute setting like below:
routes.MapRoute( name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
...this will work in the view...
var routeName = Url.RequestContext.RouteData.Values["action"].ToString();
You could pass route name through route values using default value of additional parameter:
routes.MapRoute(
name: "MyRoute",
url: "{controller}/{action}/{id}",
defaults: new { routeName = "MyRoute", controller = "Home", action = "Index", id=UrlParameter.Optional }
);
Then, it is possible to get passed value from controller context:
string routeName = ControllerContext.RouteData.Values["routeName"].ToString();
This does not directly answer the question (if you want to be pedantic); however, the real objective seems to be to get a route's base URL, given a route name. So, this is how I did it:
My route was defined in RouteConfig.cs as:
routes.MapRoute(
name: "MyRoute",
url: "Cont/Act/{blabla}",
defaults: new { controller = "Cont", action = "Act"}
);
And to get the route's base URL:
var myRoute = Url.RouteUrl("MyRoute", new { blabla = "blabla" }).Replace("blabla", "");
It gave me the route's base URL that I wanted:
/Cont/Act/
Hope this helps.
An alternative solution could be to use solution configurations:
protected override OnActionExecuting()
{
#if DEBUG
// set up stuff in the ViewData
#endif
// continue
}
There shouldn't really ever be a need to reference the route name like this - which I suppose is why MVC makes it so difficult to do this sort of thing.
another option - use MapRoute with string[] namespaces argument, then you can see your namespaces as RouteData.DataTokens["Namespaces"]

Resources