Ограничить маршрут до пространства имен контроллера в ASP.NET Core

Я пытаюсь ограничить контроллеры моих маршрутов ASP.NET Core определенным пространством имен.

В предыдущих версиях ASP.NET MVC была перегрузка, которая предоставляла параметр string[] namespaces при добавлении маршрутов. Этого нет в ASP.NET MVC 6. Итак, после некоторого поиска в Google я попытался поиграть с чем-то вроде

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

Подробнее:

У меня два полностью независимых проекта веб-API. Между прочим, некоторые маршруты идентичны в обоих (например, ~/api/ping). Эти проекты независимы в производственной среде, один является конечной точкой для пользователей, другой - конечной точкой для администраторов.

У меня также есть модульные тесты, использующие Microsoft.AspNet.TestHost. Для некоторых из этих модульных тестов требуются функциональные возможности обоих этих проектов веб-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()

person Hannes Sachsenhofer    schedule 16.12.2015    source источник
comment
несколько совпадающих маршрутов - можете ли вы поместить сюда полную ошибку с трассировкой стека?   -  person Stas Boyarincev    schedule 16.12.2015
comment
Конечно, посмотрите мой обновленный вопрос.   -  person Hannes Sachsenhofer    schedule 16.12.2015


Ответы (1)


Обновление:

Я нашел решение с помощью ActionConstraint. Вы должны добавить настраиваемый атрибут ограничения действия о повторяющихся действиях.

Пример с повторяющимися методами индекса.

Первый HomeController

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

Второй HomeController

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(...) работают независимо. А маршруты с атрибутами имеют преимущество перед обычными маршрутами.

но, похоже, это не то, что я хочу. Есть ли способ ограничить механизм маршрутизации определенным пространством имен?

Ответ разработчиков:

Вместо использования списка пространств имен для группировки контроллеров мы рекомендуем использовать области. Вы можете связать свои контроллеры (независимо от того, в какой сборке они находятся) с определенной областью, а затем создать маршрут для этой области.

Вы можете увидеть тестовый веб-сайт, на котором показан пример использования областей в 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" });
 }

Пример использования Area с маршрутизацией на основе атрибутов

//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
{
    
}
person Stas Boyarincev    schedule 16.12.2015
comment
Спасибо за отличный ответ. Это, безусловно, работает в большинстве случаев, но я думаю, что это неоптимально в моем частном случае (я должен был предоставить более подробную информацию, я исправлю это немедленно): у меня есть два полностью независимых проекта веб-API. Между прочим, некоторые маршруты идентичны (т.е. ~/api/ping). У меня есть модульные тесты, использующие Microsoft.AspNet.TestHost. Для некоторых из этих модульных тестов требуются функциональные возможности обоих этих проектов веб-API (например, для полной настройки тестового примера). Но когда я ссылаюсь на оба проекта API, TestHost запутывается из-за идентичных маршрутов. - person Hannes Sachsenhofer; 16.12.2015
comment
Большое спасибо за ваше время, использование ограничений маршрутизации здесь - отличная идея :) Я отмечу ваш ответ как решение, вы определенно заслуживаете его, но я опубликую еще один ответ, потому что мне пришлось немного изменить ваш подход, потому что dataTokens не будет работать с маршрутизацией атрибутов. - person Hannes Sachsenhofer; 17.12.2015
comment
Поднимите палец вверх до областей! - person pim; 22.08.2017
comment
Отличное решение. Я использую .NET Core 3.1, и при отладке NamespaceConstraint.IsValidForRequest я обнаружил, что routeContext.RouteData.DataTokens не содержит пространства имен, указанного в endpoints.MapControllerRoute в Startup. Есть идеи, почему его нет? - person Ryan Penfold; 19.02.2020
comment
@RyanPenfold Может быть, система маршрутизации выбирает другой маршрут без указанного DataToken - person Stas Boyarincev; 01.04.2020