Restrict route to controller namespace in ASP.NET Core

I’m trying to restrict the controllers of my ASP.NET Core routes to a certain namespace.

In previous versions of ASP.NET MVC there was an overload that provided a string[] namespaces parameter when adding routes. This is missing in ASP.NET MVC 6. So after some googling, I tried playing around with something like

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

but it doesn’t seem to do what I want. Is there a way to restrict the routing engine to a certain namespace?

Update

I just realized it may have to do something with the fact that I’m using attribute routing on each individual controller? Does attribute routing funk up the routes defined by app.UseMvc()?

Update 2

More details:

I’ve two completely independent Web API projects. Incidentally, a few of the routes are identical in both (ie. ~/api/ping). These projects are independent in Production, one is an endpoint for users, one is an endpoint for administrators.

I also have unit tests, using Microsoft.AspNet.TestHost. A few of these unit tests require functionality of both of these Web API projects (ie. need “admin” endpoint to fully setup a test case for “user”). But when I reference both API projects, the TestHost gets confused because of the identical routes and it complains about “multiple matching routes”:

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()

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Update:

I’ve found solution through using ActionConstraint. You have to add custom Action Constraint attribute about duplicate actions.

Example with duplicate Index methods.

First HomeController

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

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

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

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

First answer:

Does attribute routing funk up the routes defined by app.UseMvc()?

Attribute routing and Convention-based routing (routes.MapRoute(...) work independently. And attribute routes have advantage over convention routes.

but it doesn’t seem to do what I want. Is there a way to restrict the routing engine to a certain namespace?

Answer from developers:

Instead of using a list of namespaces to group your controllers we recommend using Areas. You can attribute your controllers (regardless of which assembly they are in) with a specific Area and then create a route for that Area.

You can see a test website that shows an example of using Areas in MVC 6 here: https://github.com/aspnet/Mvc/tree/dev/test/WebSites/RoutingWebSite.

Example using Area with convention-based routing

Controller:

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

}

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

Example using Area with attribute-based routing
//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
{
    
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x