I've updated my code to use Area as suggested but the problem still exist. /dashboard is still available.
My Controllers folder has HomeController and AccountController. I have Areas/Admin/Controllers/DashboardController.cs
Problem:
My area admin controller can be accessed like this /admin/dashboard, but the problem is it can also be accessed using /dashboard -> this should show 404
here is my RouteConfig:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "project.Controllers" }
);
AdminAreaRegistration:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "project.Areas.Admin.Controllers" }
);
}
The /dashboard call is routed by the Default routing rule.
You can make the Default not to process the calls made to the dashboard controller by adding a constraint.
For example:
In the default routing rule you can add a constraint like the following:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "project.Controllers" },
constraints: new { controller = new Constraints.IsNotDashboard() }
);
Then, you can declare the constraint like this:
using System.Web;
using System.Web.Routing;
public class IsNotDashboard : IRouteConstraint
{
public IsNotDashboard()
{
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
string controller = values["controller"].ToString().ToLower();
return controller != "dashboard";
}
}
With this constraint, all calls that match the dashboard controller will not be processed by the Default routing rule.
Thanks guys.
After searching the net, I finally found what works best for my problem.
The problem was that all controllers are being handled as well in Default route, so I just added controller constraints to Default. This way Default route will only accept request on specified controllers. Below is my new RouteConfig
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { controller = #"(Account|Manage|Home)" }
);
Related
I have a HomeController and it has many Actions in it. I would like users to visit my actions without typing Home. Here is the Route below
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I would like users not to enter controller name, which is Home in this case. How can I do that? Or is it mandatory?
You can add custom route before defult route like this:
routes.MapRoute(
"OnlyAction",
"{action}",
new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Solution 01 (attribute routing)
Add below line on the top of the other routes in RouteConfig
routes.MapMvcAttributeRoutes();
Then add an attribute route on top of each action as you want. (actions in the home controller in this case)
eg. Below code sample will remove "/Home" from http://site/Home/About and be available on http://site/About
[Route("About")]
public ActionResult About()
{
Solution 02 (using a Route constraint) [ Source ]
Add a new route mapping to RouteConfig as below. (Remember to add these specific routes before the default (generic) routes.
routes.MapRoute(
"Root",
"{action}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { isMethodInHomeController = new RootRouteConstraint<HomeController>() }
);
This will remove "Home" from all the actions (routes) of the Home controller
RootRouteConstraint class
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());
}
}
Optional Info: This line (constraint) will make sure to apply this routing only for the HomeController
new { isMethodInHomeController = new RootRouteConstraint<HomeController>
Scenario:
I have 3 Areas named- Albums, Singers , Music
Now each of these areas have controllers with same name. For instance every area has LoginController.
Now currently I am getting following exception
Multiple types were found that match the controller named 'Login'
This can happen if the route that services this request does not specify namespaces to search for a controller that matches the request.
If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
This is auto generated Configuration by Visual Studio on Area Creation
public override string AreaName
{
get
{
return "Albums"
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Albums_Default"
"Client/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
This is my intial configuration in RoutesConfig.cs
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "Application_Name" }
);
Now how to configure the routes that without any modification in url, the desired view is rendered.
Please try this one :
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL
new { controller = "Home", action = "Index", id = "" }, // Defaults
new[]{"AreasDemoWeb.Controllers"} // Namespaces
);
}
Help Link 1
Help Link2
I would like to know if it's possible to write a rule in the Route.Config file that can encompass all actions within a single controller? I have read this article but it's a little bit above me (I have just started with url rewriting and routing and the terminology is not familiar).
I have managed to change one of my actions from mydomain.co.za/Trainee/Action?id=123 to mydomain.co.za/Trainee/Action/123 but I was hoping to be able to encompass all actions in the Trainee controller, such that you can have one rule to produce Trainee/Action1/123 or Trainee/Action2/123 This is the code I used:
routes.MapRoute(
name: "ActionRewrite",
url: "Trainee/Action/{id}",
defaults: new { controller = "Trainee", action = "Action" }
);
On a side note, is it possible to hide the parameters in a URL as well, such that you can simply have mydomain.co.za/Action/ no matter what the user is doing?
Try this:
routes.MapRoute(
name: "ActionRewrite",
url: "Trainee/{action}/{id}",
defaults: new { controller = "Trainee", action = {action} }
);
Bear in mind that you can use wildcards your url match.
If you wanted you can specify that the {id} paramater remain optional which means it will also match actions that do not specify parameters.
Try this one ..
Url Rewiring in using custom id and name .
Add in app startup Route config .
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "contactus",
url: "contactus",
defaults: new { Controller = "Cms", action = "Index", id = (int)Common.CMSContactUs },
namespaces: new[] { "QZero.Controllers" }
);
routes.MapRoute(
name: "aboutus",
url: "aboutus",
defaults: new { Controller = "Cms", action = "Index", id = (int)Common.CMSAboutUs },
namespaces: new[] { "QZero.Controllers" }
);
routes.MapRoute(
name: "useragreement",
url: "useragreement",
defaults: new { Controller = "Cms", action = "Index", id = (int)Common.CMSUserAgreement },
namespaces: new[] { "QZero.Controllers" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "QZero.Controllers" }
);
}
}
Question background:
I'm trying to pass an variable - in this case an int 'productId' variable' in the url to a controller and action method specified in the ActionLink method.
The issue:
My routes are set as follows:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "ProductDetailHandler",
url: "{controller}/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional }
);
My 'ActionLink' in my 'Products.cshtml' view - which is returned by a controller called 'HomePageController' is set as follows:
#Html.ActionLink("Product", "ProductDetail", new { productId = (ViewBag.data as List<LoginTest.Models.HomePageItem>).First().Id })
The controller that receives the passed 'productId' along with its action method is set as follows:
public class ProductController : Controller
{
public ActionResult ProductDetail(int productId)
{
//logic
return View();
}
}
This is the issue, when looking at the URL it is shown to be redirecting to the 'HomePage' controller:
If someone could tell me why my ActionLink is not going to the Product controller that would be great.
EDIT:
This is the 'Homepage' view that I click a button to redirect me to 'product/productDetail/productId'
My route now just features the 'Default example':
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Login", action = "Login", id = UrlParameter.Optional }
);
The Action Link is now:
#Html.ActionLink("Product", "ProductDetail", "Product", new { id = (ViewBag.data as List<LoginTest.Models.HomePageItem>).First().Id })
The 'Product/ProductDetail' controller and action method now looks like:
public class ProductController : Controller
{
public ActionResult ProductDetail(int id)
{
string hold;
return View();
}
}
This still is giving me the the incorrect URL, as shown, note the 'productId' is now showing as 'length'?
Since the link is on a page rendered by HomePageController the default is to use that controller in the route. You need to use the overload that accepts a controller name
#Html.ActionLink("Your link text", "ProductDetail", "Product", new { id = 1 }, null)
As a side note, your original route table would have created /Product/ProductDetail?productId =1 with this overload because it matches the default route which is the first route in your table (the order of routes is important). In order to have /Product/ProductDetail/1, either reverse the order of the routes or just change the parameter of ProductDetail to int id rather than int productId
Make sure you are using an overload that has controllerName in it, as shown in the following screenshot.
When I remove routeValues: null, it uses a different overlaod which has routeValue as third paramater.
Try this:
routes.MapRoute(
name: "ProductDetailHandler",
url: "Product/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional }
);
routes.MapRoute
(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Not sure what you are trying to do with the Login controller. Maybe you can put log-in on your Home page or redirect the Home/Index to Login.
Also you can specify the default namespace if it doesn't find your controller:
routes.MapRoute(
name: "ProductDetailHandler",
url: "Product/{action}/{productId}",
defaults: new { controller = "Product", action = "ProductDetail", productId = UrlParameter.Optional },
new string[] { "MyProject.Controllers" }
);
routes.MapRoute
(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "MyProject.Controllers" }
);
I have multiple routes configured, but for some reason, despite the rules addressing different Controllers and different Views, different links are routing to the same view. Please see below, I have included my RouteConfig file and example links below:
RouteConfig.cs
using System.Web.Mvc;
using System.Web.Routing;
namespace WebApplication1
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Account",
url: "Account/{action}/{id}",
defaults: new { controller = "Account", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Member",
url: "Member/{action}/{id}",
defaults: new { controller = "Member", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Root",
url: "{action}/{id}",
defaults: new { controller = "Home", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Details",
url: "{controller}/{action}/{u}",
defaults: new
{
controller = "Member",
action = "Details",
u = UrlParameter.Optional
}
);
routes.MapRoute(
name: "Article",
url: "{Home}/{Article}/{id}/{articleName}",
defaults: new { controller = "Home", action = "Article" }
);
routes.MapRoute(
name: "Item",
url: "{News}/{Item}/{id}/{itemName}",
defaults: new { controller = "News", action = "Item" }
);
}
}
}
Links
http://localhost:11508/Home/Article/2/Participate
http://localhost:11508/News/Item/2/Second-Test
As so can see, the links and rules are most certainly unique but for some reason the Item rule is being ignored, it is simply passing Id 2 to the Home/Article view.
You shouldn't include controller / action names in brackets - just pass them as is, so that path can be matched. Your last two routes should look like this:
routes.MapRoute(
name: "Article",
url: "Home/Article/{id}/{articleName}",
defaults: new { controller = "Home", action = "Article" }
);
routes.MapRoute(
name: "Item",
url: "News/Item/{id}/{itemName}",
defaults: new { controller = "News", action = "Item" }
);
Also, it is good to place such specific routes before any other routes, not after default routes.
UPDATE
Basically it should be separate question, but it is easier to just answer it here.
From comment:
how I can get http://localhost:11508/Member/Details?u=testuser to be routed to http://localhost:11508/Member/Details/testuser instead of a showing parameter.
Create controller action which accepts this parameter, like this one:
public ActionResult Details(string u, ...)
{
var model = new ...
...
return View(model);
}
Register route, which accepts u parameter as URL part, like this one
routes.MapRoute(
name: "MyRoute",
url: "Member/Details/{u}",
defaults: new { controller = "Member", action = "Details", u = UrlParameter.Optional }
);
Here {u} actually declares parameter name, and how it should be used (parsed / rendered) inside URL.
Render link to the URL like this one:
linktext
In all these steps, u is that name of parameter which you will use.
The Mapping takes the first matching rule.
The "Item"-Route would never be used because the Article-Root will catch all request that could match "Item"-Route.
Check the order of the routes AND delete the {} surrounding news.
routes.MapRoute(
name: "Item",
url: "News/Item/{id}/{itemName}",
defaults: new { controller = "News", action = "Item" }
);
Your problem is in the order in which you are registering your routes. The rule is that you should register them from the most specific to the least. In other words, your "default" route(s) should be the very last.
With how you have it right now, MVC gets a hit on your default route, because your item route matches that, so once it hits on that, it stops looking for other routes and uses it.
Move your item route up to the top of your RegisterRoutes method and it should work fine.