【问题标题】:Restrict route to controller namespace in ASP.NET Core限制路由到 ASP.NET Core 中的控制器命名空间
【发布时间】:2023-03-16 03:00:01
【问题描述】:

我正在尝试将我的 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()定义的路由进行了funk?

更新 2

更多详情:

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

我也有单元测试,使用Microsoft.AspNet.TestHost。其中一些单元测试需要这两个 Web API 项目的功能(即需要“admin”端点来为“user”完全设置测试用例)。但是当我引用这两个 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()

【问题讨论】:

  • “多个匹配路由” - 你能把堆栈跟踪的完整错误放在这里吗?
  • 好的,请看我更新的问题。

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


【解决方案1】:

更新:

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

具有重复索引方法的示例。

第一个 HomeController

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;
        }
    }
}

第一个答案:

属性路由是否会影响 app.UseMvc() 定义的路由?

属性路由和基于约定的路由 (routes.MapRoute(...) 独立工作。而且属性路由比约定路由更有优势。

但它似乎没有做我想要的。有没有办法将路由引擎限制到某个命名空间?

Answer from developers:

我们建议使用区域,而不是使用命名空间列表来对控制器进行分组。您可以将您的控制器(无论它们位于哪个程序集)归属于特定区域,然后为该区域创建路线。

您可以在此处查看显示在 MVC 6 中使用区域的示例的测试网站:https://github.com/aspnet/Mvc/tree/dev/test/WebSites/RoutingWebSite

将 Area 与基于约定的路由结合使用的示例

控制器:

//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
{
    
}

【讨论】:

  • 感谢您的出色回答。这在大多数情况下确实有效,但我认为在我的特殊情况下它不是最佳的(我应该提供更多细节,我会立即修复):我有两个完全独立的 Web API 项目。顺便说一句,一些路线是相同的(即~/api/ping)。我有单元测试,使用Microsoft.AspNet.TestHost。其中一些单元测试需要这两个 Web API 项目的功能(例如,完全设置测试用例)。但是当我引用这两个 API 项目时,TestHost 会因为相同的路线而感到困惑。
  • 非常感谢您抽出宝贵时间,在这里使用路由约束是一个绝妙的主意 :) 我会将您的答案标记为解决方案,您绝对应得的,但也会发布另一个答案,因为我必须稍微改变您的方法,因为 dataTokens 不适用于属性路由。
  • 对区域点赞!
  • 很好的解决方案。我使用的是 .NET Core 3.1,在调试 NamespaceConstraint.IsValidForRequest 时发现,routeContext.RouteData.DataTokens 不包含 Startup 中的 endpoints.MapControllerRoute 中指定的命名空间。任何想法为什么它不存在?
  • @RyanPenfold 也许路由系统在没有指定 DataToken 的情况下选择了不同的路由
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-05
  • 2012-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-07
  • 1970-01-01
相关资源
最近更新 更多