Reputation: 12764
I'm developing an online marketplace system in ASP.NET MVC where Users are able to create their own shops in my website with their own unique address (e.g MySite.com/UserShop) like some social medias such as Instagram which users' pages would be like "Instagram.com/YourPage".
I wanna do something similar in ASP.NET MVC. This is my stucture:
I have multiple controllers such as Home, Panel and ... and I also have a controller named ShopController which I excepted its Index action method to show users' pages (shops). It's like this:
[RoutePrefix("")]
public class ShopController : Controller
{
[Route("{shopName}")]
public ActionResult Index(string shopName)
{
return View();
}
}
When I enter the some address http://MySite/UserPage
it works fine, but when I want to open the urls of my own website like http://MySite/Panel
- I get exception: Multiple controller types were found that match the URL
.
I think I have to set order for controllers and action methods (First my own action methods, and then ShopController), I know it can be done within single controllers by using [Order] attribute, but I don't know how to do this across all controllers.
How can I fix this and make it work properly?
Upvotes: 1
Views: 2580
Reputation: 247153
Because of the desired flexibility with the custom user routes, you will end up with route conflicts as the user route is too general, which will make it also match your other site routes and cause route conflicts.
Attribute routes are checked before convention-based routes, so the shop controller will catch all requests.
Convention based routes would need to be used in this case if you want user routes to be on the root of the site. This is because the order in which routes are added to the route table are important as general routes will match before more specialised/targeted routes.
Consider mixing attribute routing and convention-based routing where the custom user routes will use convention based routes while your other controllers will use attribute routing.
[RoutePrefix("Home")]
public class HomeController : Controller {
[HttpGet]
[Route("")] //GET home
[Route("~/", Name = "default")] // Site root
public ActionResult Index() {
return View();
}
[HttpGet]
[Route("contact")] //GET home/contact
[Route("~/contact")] //GET contact
public ActionResult Contact() {
return View();
}
[HttpGet]
[Route("about")] //GET home/about
[Route("~/about")] //GET about
public ActionResult About() {
return View();
}
//...other actions
}
[RoutePrefix("Panel")]
public class PanelController : Controller {
[HttpGet]
[Route("")] //GET panel
public ActionResult Index() {
return View();
}
[HttpGet]
[Route("another-action")] //GET panel/another-action
public ActionResult Other() {
return View();
}
//...other actions
}
The attribute routes, because they are registered before convention routes will match before the user defined routes, which can use convention-based routing
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
//Attribute routing
routes.MapMvcAttributeRoutes();
//Convention based routing
//User route:
routes.MapRoute(
name: "UserRoot",
url: "{shopName}/{action}", //eg MySite.com/UserShop
defaults: new { controller = "Shop", action = "Index"}
);
//Catch-All InValid (NotFound) Routes
routes.MapRoute(
name: "NotFound",
url: "{*url}",
defaults: new { controller = "Error", action = "NotFound" }
);
}
}
The attribute routes would then need to be removed from the shop controller to avoid conflicts with the other site controllers
public class ShopController : Controller {
//eg MySite.com/UserShop
//eg MySite.com/UserShop/index
public ActionResult Index(string shopName) {
return View();
}
//eg MySite.com/UserShop/contact
public ActionResult Contact(string shopName) {
return View();
}
//eg MySite.com/UserShop/about
public ActionResult About(string shopName) {
return View();
}
//...other actions
}
So now calls to MySite.com/UserShop
will be routed to the correct shop controller and still allow the site controllers to be accessed.
While it is however more labour intensive than the convention based user routes, that is the trade off to get the desired routing behavior.
Upvotes: 3