How to modify ASP NET Web API Route matching to allow parameters with slashes inside?

余生颓废 提交于 2020-01-24 15:10:43

问题


We're using RavenDB on the backend and so all the DB keys are strings that contain forward slashes e.g. users/1.

AFAIK there's no way to use the default ASP NET Web API route matching engine to match a parameter like this, without using a catch-all which means the key must be the last thing on the URL. I tried adding a regex constraint of users/d+ but it didn't seem to make a difference, the route wasn't matched.

What bits would I have to replace to do just enough custom route matching to allow this, preferably without crippling the rest of the route matching. For example, using url: "{*route}" and a custom constraint that did full regex matching would work but may work unexpectedly with other route configurations.

If your answer comes with some sample code, so much the better.


回答1:


It seems that it is possible to do this by defining a custom route. In MVC4 (last stable released 4.0.30506.0), it is not possible to do by implementing IHttpRoute as per specification but by defining a custom MVC-level Route and adding it directly to the RouteTable. For details see 1, 2. The RegexRoute implementation below is based on the implementation here with mods from the answer here.

Define RegexRoute:

public class RegexRoute : Route
{
    private readonly Regex urlRegex;

    public RegexRoute(string urlPattern, string routeTemplate, object defaults, object constraints = null)
        : base(routeTemplate, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(), HttpControllerRouteHandler.Instance)
    {
        urlRegex = new Regex(urlPattern, RegexOptions.Compiled);
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        string requestUrl = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;

        Match match = urlRegex.Match(requestUrl);

        RouteData data = null;

        if (match.Success)
        {
            data = new RouteData(this, RouteHandler);

            // add defaults first
            if (null != Defaults)
            {
                foreach (var def in Defaults)
                {
                    data.Values[def.Key] = def.Value;
                }
            }

            // iterate matching groups
            for (int i = 1; i < match.Groups.Count; i++)
            {
                Group group = match.Groups[i];

                if (group.Success)
                {
                    string key = urlRegex.GroupNameFromNumber(i);

                    if (!String.IsNullOrEmpty(key) && !Char.IsNumber(key, 0)) // only consider named groups
                    {
                        data.Values[key] = group.Value;
                    }
                }
            }
        }

        return data;
    }
}

Add this DelegatingHandler to avoid a NullReference due to some other bug:

public class RouteByPassingHandler : DelegatingHandler
    {
        protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            HttpMessageInvoker invoker = new HttpMessageInvoker(new HttpControllerDispatcher(request.GetConfiguration()));
            return invoker.SendAsync(request, cancellationToken);
        }
    }

Add handler and route directly to the RouteTable:

RouteTable.Routes.Add(new RegexRoute(@"^api/home/index/(?<id>\d+)$", "test", new { controller = "Home", action = "Index" }));

        config.MessageHandlers.Add(new RouteByPassingHandler());

Et voila!

EDIT: This solution has problems when the API is self-hosted (instead of using a WebHost) and requires further work to make it work with both. If this is interesting to anyone, please leave a comment and I'll post my solution.



来源:https://stackoverflow.com/questions/17856926/how-to-modify-asp-net-web-api-route-matching-to-allow-parameters-with-slashes-in

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!