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.