1

I have an MVC controller action with one parameter that should only be called with certain values for that parameter (null/empty and some specific strings), in other cases it should not be hit (404 mostly). I've tried using a RegexRouteConstraint like below. But that doesn't filter the specific strings.

var route = new Route("{soortAanbod}", new MvcRouteHandler())
{
    Defaults = new RouteValueDictionary(new { controller = "Homepage", action = "Index", soortAanbod = UrlParameter.Optional }),
    Constraints = new RouteValueDictionary(new { soortAanbod = new RegexRouteConstraint("a|b|c|d|e") }),
    DataTokens = new RouteValueDictionary { { "area", context.AreaName } }
};
context.Routes.Add("Homepage_soortAanbod", route);

The controller looks like this: public ActionResult Index(string soortAanbod)

I've also tried using an action filter but that messes up other other filters. How can I make this route only match on the specified values for soortAanbod?

mnwsmit
  • 1,198
  • 2
  • 15
  • 31
  • 1
    if this is one specific action, you have the choice to handle it directly in your action by code – cdie Mar 21 '16 at 15:22
  • It's several actions using the same constraint. Code can be an option but it's rather un-MVC – mnwsmit Mar 21 '16 at 20:08

3 Answers3

1

I think you can try attribute routing or maybe write your own attribute for action to check params or redirect somewhere.

public class SomeAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var yourstring = filterContext.RequestContext.HttpContext.Request.QueryString["string"];
        if (!string.IsNullOrWhiteSpace(yourstring))
        {
            if (yourstring is not ok) 
            filterContext.Result =
                new RedirectToRouteResult(
                    new RouteValueDictionary
                    {
                        {"controller", "SomeCtrl"},
                        {"action", "SomeAction"}
                    });
        }
        base.OnActionExecuting(filterContext);

    }
romandemidov
  • 76
  • 1
  • 5
  • I tried something like this (with a NotFoundResult) but it messes up the filter pipeline that is already present. – mnwsmit Mar 21 '16 at 20:10
1

You can create a custom constraint and use attribute routing, use the custom constraint there and make the constraint constructor accepts the list of strings that you want to avoid

Custom MVC Route Constraint

Custom Constraint with Attribute Routing

Haitham Shaddad
  • 4,336
  • 2
  • 14
  • 19
1

You have 2 issues:

  1. Your RegEx does not contain anchors (^ and $) to delimit the string you are attempting to match. So, it is matching any string that contains any of these letters.
  2. Your URL will always match the default route, so even if your custom route doesn't match, you will still get to the page. The default route will match any URL that is 0, 1, 2, or 3 segments in length.

You can get around this by removing your custom route and using an IgnoreRoute (which behind the scenes uses a StopRoutingHandler) to prevent those specific URLs from matching.

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.IgnoreRoute("Homepage/Index/{soortAanbod}", 
            new { soortAanbod = new NegativeRegexRouteConstraint(@"^a$|^b$|^c$|^d$|^e$") });

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

There is a caveat, though. RegEx's are not very good at doing negative matches, so if you are not a RegEx guru the simplest solution is to build a NegativeRegexRouteConstraint to handle this scenario.

public class NegativeRegexRouteConstraint : IRouteConstraint
{
    private readonly string _pattern;
    private readonly Regex _regex;

    public NegativeRegexRouteConstraint(string pattern)
    {
        _pattern = pattern;
        _regex = new Regex(pattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.Compiled);
    }

    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (parameterName == null)
            throw new ArgumentNullException("parameterName");
        if (values == null)
            throw new ArgumentNullException("values");

        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
            return !_regex.IsMatch(valueString);
        }
        return true;
    }
}
Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • It was the anchors. Silly bug. This website doesn't have the default route, so only the anchors was enough. Thanks! – mnwsmit Mar 22 '16 at 06:56