简体   繁体   中英

ASP.NET MVC constraint for query string parameter

I have 2 actions

public Action HasNumber(string name) { ... }

public Action DoesntHaveNumer(string name) { ... }

Is it possible to create such route that it will hit one or anothere depending on the value of name parameter?

Example:

myApp/Home?name=qwe will hit DoesntHaveNumer

myApp/Home?name=q2e will hit HasNumber

name is a mandatory parameter

The default Route class does not interact with the query string. But, you can inherit it to add additional functionality.

using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

public class NumericCheckingQueryStringValueRoute : Route
{
    private readonly string queryStringKey;
    private readonly RouteValueDictionary defaultsIfNumeric;
    private readonly RouteValueDictionary defaultsIfNonNumeric;

    public NumericCheckingQueryStringValueRoute(
        string url,
        string queryStringKey,
        object defaultsIfNumeric,
        object defaultsIfNonNumeric)
        : base(url, new MvcRouteHandler())
    {
        this.queryStringKey = queryStringKey;
        this.defaultsIfNumeric = new RouteValueDictionary(defaultsIfNumeric);
        this.defaultsIfNonNumeric = new RouteValueDictionary(defaultsIfNonNumeric);
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var result = base.GetRouteData(httpContext);

        // If it matches, the result will be non-null
        if (result != null)
        {
            var newResult = new RouteData(this, new MvcRouteHandler());

            // Copy the route values from the base class
            foreach (var value in result.Values)
            {
                newResult.Values[value.Key] = value.Value;
            }
            // Copy the data tokens from the base class
            foreach (var token in result.DataTokens)
            {
                newResult.DataTokens[token.Key] = token.Value;
            }

            var requestQueryString = httpContext.Request.QueryString;

            var queryStringValue = requestQueryString[this.queryStringKey];

            if (string.IsNullOrEmpty(queryStringValue))
            {
                return null;
            }

            RouteValueDictionary defaultsToUse;
            if (ContainsDigits(queryStringValue))
            {
                defaultsToUse = this.defaultsIfNumeric;
            }
            else
            {
                defaultsToUse = this.defaultsIfNonNumeric;
            }

            // Copy the numeric defaults to route values
            foreach (var value in defaultsToUse)
            {
                newResult.Values[value.Key] = value.Value;
            }

            // We matched, return the result
            return newResult;
        }

        // Return null if no match
        return null;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        // We just pass this through. If you add the value as a route value
        // when building ActionLink or RouteLink, it will automatically make
        // it into a query string value because it is not part of the url argument.
        return base.GetVirtualPath(requestContext, values);
    }

    // Fastest way to check for digits
    // http://stackoverflow.com/questions/7461080/fastest-way-to-check-if-string-contains-only-digits
    private bool ContainsDigits(string str)
    {
        foreach (char c in str)
        {
            if (c >= '0' && c <= '9')
                return true;
        }

        return false;
    }
}

Usage

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

        routes.Add(
            name: "CheckNumericName",
            item: new NumericCheckingQueryStringValueRoute(
                url: "Home",
                queryStringKey: "name",
                defaultsIfNumeric: new { controller = "Home", action = "HasNumber" },
                defaultsIfNonNumeric: new { controller = "Home", action = "DoesntHaveNumer" }
                )
            );

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

And to build links to the page, you will need to add the query string value to the route values collection. You should use RouteLink since you have it going to 2 different action methods.

@Html.RouteLink("Link with number", "CheckNumericName", new { name = "q2e" })
@Html.RouteLink("Link with no number", "CheckNumericName", new { name = "qwe" })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM