在 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 的调用将被路由到正确的商店控制器,并且仍然允许访问站点控制器。

虽然它比基于约定的用户路由更加劳动密集,但这是获得所需路由行为的权衡。