在ASP.NET MVC中为所有控制器设置控制器路由顺序

时间:2018-04-20 13:14:05

标签: c# asp.net-mvc asp.net-mvc-routing

我正在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时,它可以正常工作,但是当我想打开我自己网站的网址http://MySite/Panel时 - 我得到例外:Multiple controller types were found that match the URL

我想我必须为控制器和动作方法设置顺序(首先是我自己的动作方法,然后是ShopController),我知道它可以通过使用[Order]属性在单个控制器中完成,但我不知道如何在所有控制器上执行此操作。

如何解决此问题并使其正常工作?

1 个答案:

答案 0 :(得分:3)

由于自定义用户路由具有所需的灵活性,因此当用户路由过于笼统时,最终会出现路由冲突,这会使其与其他站点路由匹配并导致路由冲突。

在基于约定的路线之前检查属性路线,因此车间控制器将捕获所有请求。

如果您希望用户路由位于站点的根目录,则需要在此情况下使用基于约定的路由。这是因为路由添加到路由表的顺序很重要,因为一般路由将在更专业/目标路由之前匹配。

考虑混合属性路由和基于约定的路由,其中​​自定义用户路由将使用基于约定的路由,而其他控制器将使用属性路由。

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

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