在 ASP.NET MVC 中为所有控制器的路由设置顺序
Set Order for route of controllers across all of them in ASP.NET MVC
我正在 ASP.NET MVC 中开发一个在线市场系统,用户可以在其中使用自己独特的地址(例如 MySite.com/UserShop)在我的网站上创建自己的商店,就像一些社交网站一样Instagram 等媒体,用户的页面会像 "Instagram.com/YourPage".
我想在 ASP.NET MVC 中做类似的事情。这是我的结构:
我有多个控制器,例如 Home、Panel 和...,我还有一个名为 ShopController 的控制器,我将它的 Index 操作方法排除在外,以显示用户的页面(商店)。是这样的:
[RoutePrefix("")]
public class ShopController : Controller
{
[Route("{shopName}")]
public ActionResult Index(string shopName)
{
return View();
}
}
当我输入一些地址 http://MySite/UserPage
时,它工作正常,但是当我想打开我自己网站的 url 时,如 http://MySite/Panel
- 我得到异常:Multiple controller types were found that match the URL
。
我想我必须为控制器和操作方法设置顺序(首先是我自己的操作方法,然后是 ShopController),我知道它可以通过使用 [Order] 属性在单个控制器中完成,但我不知道知道如何在所有控制器上执行此操作。
我该如何解决这个问题并使其正常工作?
由于自定义用户路由所需的灵活性,您最终会遇到路由冲突,因为用户路由过于笼统,这将使它也匹配您的其他站点路由并导致路由冲突。
在基于约定的路由之前检查属性路由,因此商店控制器将捕获所有请求。
如果您希望用户路由位于站点的根目录中,则需要在这种情况下使用基于约定的路由。这是因为路由添加到路由 table 的顺序很重要,因为一般路由将在更多 specialised/targeted 路由之前匹配。
考虑混合属性路由和基于约定的路由,其中自定义用户路由将使用基于约定的路由,而您的其他控制器将使用属性路由。
[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
}
属性路由,因为它们是在约定路由之前注册的,所以会在用户自定义路由之前匹配,可以使用约定路由
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" }
);
}
}
然后需要从商店控制器中删除属性路由,以避免与其他站点控制器发生冲突
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
}
所以现在对 MySite.com/UserShop
的调用将被路由到正确的商店控制器,并且仍然允许访问站点控制器。
虽然它比基于约定的用户路由更加劳动密集,但这是获得所需路由行为的权衡。
我正在 ASP.NET MVC 中开发一个在线市场系统,用户可以在其中使用自己独特的地址(例如 MySite.com/UserShop)在我的网站上创建自己的商店,就像一些社交网站一样Instagram 等媒体,用户的页面会像 "Instagram.com/YourPage".
我想在 ASP.NET MVC 中做类似的事情。这是我的结构:
我有多个控制器,例如 Home、Panel 和...,我还有一个名为 ShopController 的控制器,我将它的 Index 操作方法排除在外,以显示用户的页面(商店)。是这样的:
[RoutePrefix("")]
public class ShopController : Controller
{
[Route("{shopName}")]
public ActionResult Index(string shopName)
{
return View();
}
}
当我输入一些地址 http://MySite/UserPage
时,它工作正常,但是当我想打开我自己网站的 url 时,如 http://MySite/Panel
- 我得到异常:Multiple controller types were found that match the URL
。
我想我必须为控制器和操作方法设置顺序(首先是我自己的操作方法,然后是 ShopController),我知道它可以通过使用 [Order] 属性在单个控制器中完成,但我不知道知道如何在所有控制器上执行此操作。
我该如何解决这个问题并使其正常工作?
由于自定义用户路由所需的灵活性,您最终会遇到路由冲突,因为用户路由过于笼统,这将使它也匹配您的其他站点路由并导致路由冲突。
在基于约定的路由之前检查属性路由,因此商店控制器将捕获所有请求。
如果您希望用户路由位于站点的根目录中,则需要在这种情况下使用基于约定的路由。这是因为路由添加到路由 table 的顺序很重要,因为一般路由将在更多 specialised/targeted 路由之前匹配。
考虑混合属性路由和基于约定的路由,其中自定义用户路由将使用基于约定的路由,而您的其他控制器将使用属性路由。
[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
}
属性路由,因为它们是在约定路由之前注册的,所以会在用户自定义路由之前匹配,可以使用约定路由
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" }
);
}
}
然后需要从商店控制器中删除属性路由,以避免与其他站点控制器发生冲突
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
}
所以现在对 MySite.com/UserShop
的调用将被路由到正确的商店控制器,并且仍然允许访问站点控制器。
虽然它比基于约定的用户路由更加劳动密集,但这是获得所需路由行为的权衡。