将路由限制为ASP.NET Core中的控制器命名空间

本文关键字:Core 控制器 命名空间 NET ASP 路由 | 更新日期: 2023-09-27 18:27:08

我试图将ASP.NET Core路由的控制器限制到某个命名空间。

在ASP.NET MVC的早期版本中,有一个重载在添加路由时提供string[] namespaces参数。ASP.NET MVC 6中缺少此项。所以在谷歌上搜索了一下之后,我试着玩之类的东西

app.UseMvc(routes => {
    var dataTokens = new RouteValueDictionary {
        {
            "Namespaces", new[] {"ProjectA.SomeNamespace.Controllers"}
        }
    };
    routes.MapRoute(
         name: "default",
         template: "{controller=Home}/{action=Index}/{id?}",
         defaults: null,
         constraints: null,
         dataTokens: dataTokens
    );
});

但它似乎没有达到我想要的效果。有没有一种方法可以将路由引擎限制在某个命名空间内?

更新

我刚刚意识到这可能与我在每个单独的控制器上使用属性路由这一事实有关?属性路由是否阻碍了app.UseMvc()定义的路由?

更新2

更多详细信息:

我有两个完全独立的Web API项目。顺便说一句,其中一些路由在两者中是相同的(即~/api/ping)。这些项目在生产中是独立的,一个是用户的端点,另一个是管理员的端点。

我也有使用Microsoft.AspNet.TestHost的单元测试。其中一些单元测试需要这两个Web API项目的功能(即,需要"admin"端点来为"用户"完全设置测试用例)。但当我引用两个API项目时,TestHost会因为相同的路由而感到困惑,并抱怨"多个匹配的路由":

Microsoft.AspNet.Diagnostics.DeveloperExceptionPageMiddleware: Error: An unhandled exception has occurred while executing the request
Microsoft.AspNet.Mvc.Infrastructure.AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:
    ProjectA.SomeNamespace.Controllers.PingController.Ping
    ProjectB.SomeNamespace.Controllers.PingController.Ping
at Microsoft.AspNet.Mvc.Infrastructure.DefaultActionSelector.SelectAsync(RouteContext context)
at Microsoft.AspNet.Mvc.Infrastructure.MvcRouteHandler.<RouteAsync>d__6.MoveNext()

将路由限制为ASP.NET Core中的控制器命名空间

更新:

我已经通过使用ActionConstraint找到了解决方案。您必须添加关于重复操作的自定义"操作约束"属性。

具有重复Index方法的示例。

第一个家庭控制器

namespace WebApplication.Controllers
{
    public class HomeController : Controller
    {
        [NamespaceConstraint]
        public IActionResult Index()
        {
            return View();
        }
    }
}

第二个家庭控制器

namespace WebApplication
{
    public class HomeController : Controller
    {
        [NamespaceConstraint]
        public IActionResult Index()
        {
            return View();
        }
    }
}

配置路由

app.UseMvc(cR =>
   cR.MapRoute("default", "{controller}/{action}", null, null, 
   new { Namespace = "WebApplication.Controllers.HomeController" }));

行动约束

namespace WebApplication
{
    public class NamespaceConstraint : ActionMethodSelectorAttribute
    {
        public override bool IsValidForRequest(RouteContext routeContext, ActionDescriptor action)
        {
            var dataTokenNamespace = (string)routeContext.RouteData.DataTokens.FirstOrDefault(dt => dt.Key == "Namespace").Value;
            var actionNamespace = ((ControllerActionDescriptor)action).MethodInfo.DeclaringType.FullName;
            return dataTokenNamespace == actionNamespace;
        }
    }
}

第一个答案:

属性路由会使应用程序定义的路由变快吗。是否使用Mvc()?

属性路由和基于约定的路由(routes.MapRoute(...)独立工作。属性路由相对于常规路由具有优势。

但它似乎没有达到我想要的效果。有没有一种方法可以将路由引擎限制在某个命名空间内?

开发者的回答:

我们建议使用区域,而不是使用名称空间列表来对控制器进行分组。您可以将控制器(无论它们在哪个部件中)与特定的"区域"相关联,然后为该"区域"创建管线。

你可以在这里看到一个测试网站,展示了在MVC 6中使用Areas的例子:https://github.com/aspnet/Mvc/tree/dev/test/WebSites/RoutingWebSite.

使用区域和基于约定的路由的示例

控制器:

//Reached through /admin/users
//have to be located into: project_root/Areas/Admin/
[Area("Admin")]
public class UsersController : Controller
{
}

配置基于约定的路由:

 app.UseMvc(routes =>
 {
         routes.MapRoute(
         "areaRoute",
         "{area:exists}/{controller}/{action}",
         new { controller = "Home", action = "Index" });
 }

使用区域和基于属性的路由的示例

//Reached through /admin/users
//have to be located into: project_root/Areas/Admin/
[Area("Admin")]
[Route("[area]/[controller]/[action]", Name = "[area]_[controller]_[action]")]
public class UsersController : Controller
{
    
}