2

I'd like to setup a custom route constraint that would allow me to decorate a controller with an attribute so that I don't have to pass in string to the route and remember to update it with new controller names.

I thought I could setup use the IRouteConstraint but I can't seem to get the attribute. Perhaps I'm just missing something obvious here.

routes.MapRoute("test", 
    "foo/{controller}/{action}/{id}",
    new { controller = "Home", action = "Index", id = UrlParameter.Optional },
    new { controller = new TestConstraint()}
);

routes.MapRoute("Default", "{controller}/{action}/{id}", 
    new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);



public class TestConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, 
        RouteValueDictionary values,
        RouteDirection routeDirection)
    {
        return false;
    }
}

[AttributeUsage(AttributeTargets.Class)]
public class CustomConstraintControllerAttribute : Attribute
{

}

[CustomConstraintController]
public class TestController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

Edit:

Current Method:

routes.MapSubdomainRoute("Store",
    "{store}/{controller}/{action}/{id}",
    new {controller = "Home", action = "Index", id = UrlParameter.Optional},
    new {controller = "StoreHome|Contacts|..." }
    );

This route configuration ensures that a url must match subdomain.test.com/GiggleInc/Contacts or subdomain.test.com/GiggleInc/StoreHome.

subdomain.test.com/GiggleInc/StoreHome
MapSubdomainRoute /{store}  /{controller}/{action}/{id}

This method requires that each controller that should be used this way must be added to the controller constraint.

As I mentioned in the comments, I want to replace the hard coded strings for an attribute or something similar.

Eonasdan
  • 7,563
  • 8
  • 55
  • 82
  • Routing is completely finished *before* the controller is selected. Exactly what is it you hope to achieve with your constraint? – NightOwl888 Jan 22 '16 at 18:44
  • Do note that you can completely setup routing (including constraints) in MVC 5 using [Attribute Routes](http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx), which simply put your route config along with your controller. – NightOwl888 Jan 22 '16 at 18:45
  • that's what I was afraid. From my research that's what I figured the answer was. We're using subdomains in our project and certain controllers require a subdomain to be present. Currently, we stuff the constraint with the controller class names as a string. I was hoping to decorate the class instead – Eonasdan Jan 22 '16 at 18:47
  • although if I put a break point on the `return false` it is hit at the start of the request. I thought I might be able to use something like `ControllerDescriptor` like in an `AuthorizeAttribute` – Eonasdan Jan 22 '16 at 18:49
  • Please update your question with details about what you are trying to achieve. It is unclear what case you have here that `AuthorizeAttribute` doesn't cover already. What exactly do you expect the application to do if a user tries to access a controller that they are "not allowed" to? – NightOwl888 Jan 22 '16 at 18:55
  • `AuthorizeAttribute` was just an example. For in `AuthorizeAttribute` you can get the attributes that decorate an action or class. In the `Match` method, I wanted to check the controller to see if it had `CustomConstraintController` – Eonasdan Jan 22 '16 at 18:57
  • The purpose of a constraint is to tell .NET routing whether to use the current route (true) or to attempt the next registered route (false). Since the route isn't even selected at that point, it would not be possible to tell what controller to use (unless you scan them all or add the word "Controller" to the value passed in on the URL). Still very unclear what use case you have that made you go down this road. – NightOwl888 Jan 22 '16 at 19:05
  • I know what a constraint does. Please see the edit – Eonasdan Jan 22 '16 at 19:34
  • Still not enough info to tell what you are doing - where is the code for `MapSubdomainRoute`? But, I suspect your problem can be resolved with the `ActionExistsConstraint` provided in [this answer](http://stackoverflow.com/questions/32919876/problems-with-creating-two-routes-which-wont-generate-404-error-in-asp-net-mvc). – NightOwl888 Jan 22 '16 at 21:46

2 Answers2

1

First, thank you for this sweet line of code I didn't know was possible:

constraints: new { controller = "StoreHome|Contacts" }

I didn't know I could filter my MapRoute to a list of Controllers so easily.
Second, you don't need to implement a custom IRouteConstraint for this.
MVC offers the Attribute-Routing you are looking for.

You may even include Default/Optional Values, just like in your MapRoute.
Decorate your Controller Class like so:

[RoutePrefix("{store}/Test")]
[Route("{action=Index}/{id?}")]//Without this, you need to define "[Route]" above every Action-Method.
public class TestController : Controller
{
    public ActionResult Index(string store)//Adding "string store" is optional.
    {
        return View();
    }
}

That's it.
Remember to add the "store" Parameter in all your Actions under each Controller (but it is not Required).

Note:
If you use Attributes instead of MapRoute, then you will not be able to hit the Controller without the "store" Prefix.
With the Custom and Default MapRoutes, you could have accessed your controller either way.
By decorating your Controller with these Attributes, you now force it to only use this exact path.
This may be what you want, but if you start IIS Express from Visual Studio on one of your Views, it will not find it, because Visual Studio doesn't know to add the RoutePrefix for you.

See this link for more information about Attribute-Routing:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/

MikeTeeVee
  • 18,543
  • 7
  • 76
  • 70
  • where did you use `constraints: new { controller = "StoreHome|Contacts" }` – Seabizkit Sep 26 '19 at 20:25
  • @Seabizkit I used it in the `routes.MapRoute()` function. Please see my other answer for a better example: https://stackoverflow.com/a/52033013/555798 – MikeTeeVee Sep 27 '19 at 09:00
  • thank you, hard to find information around how you could/should structure your project/projects when your using more advance context relative routing and have areas already, i have gotten it to work, but would be nice to find info examples on how to structure. trying to ask got me no where https://softwareengineering.stackexchange.com/questions/398700/mvc-routing-attribute-routing-and-project-structure – Seabizkit Sep 27 '19 at 09:12
0

Try this...

public class TestRouteAttribute : RouteFactoryAttribute
{      
    public TestRouteAttribute(string template) : base(template) { }

    public override RouteValueDictionary Constraints
    {
        get
        {
            var constraints = new RouteValueDictionary();
            constraints.Add("TestConstraint", new TestConstraint());
            return constraints;
        }
    }
}

Then you should be able to decorate your action methods using [TestRoute]

By the way, would love to know how to accomplish this in asp.net core if anyone knows.

ctorx
  • 6,841
  • 8
  • 39
  • 53