Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Api Routing Handler Change Route Data

Within web api route I have created the following route and route handler.

Im trying to intercept the request and change some of the route data before it hits the actual method.

However the below doesnt quite work as I expected, when running and making requests to the api, it returns a 404 No route providing a controller name was found to match request.

Is this actually possible to change the route data? I feel I am close to a solution but missing something.

Below is my code:

public class CustomHander : DelegatingHandler {
    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {

        var routeData = request.GetRouteData();
        var subroutes = (IEnumerable<IHttpRouteData>)routeData.Values["MS_SubRoutes"];
        var route = subroutes.First();

        var msSubRoutes = new HttpRouteValueDictionary();

        var httpRouteData = new HttpRouteData(route.Route);

        foreach (var r in route.Values) {
            httpRouteData.Values.Add(r.Key, r.Value.ToString().Replace("|", "/"));
        }

        msSubRoutes.Add("MS_SubRoutes", httpRouteData);
        var newRoute = new HttpRouteData(routeData.Route, msSubRoutes);

        request.SetRouteData(newRoute);
        var test = request.GetRouteData();

        var resp = await base.SendAsync(request, cancellationToken);
        return resp;
    }
}

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional, controller = "Vehicles" }
        );

        config.MessageHandlers.Add(new CustomHander());
    }
}

If i remove the line of code below, then it works as normal, but obviously without change the route data.

request.SetRouteData(newRoute);

Example of Action Result on controller:

[Route("api/Makes")]
[HttpGet]
public IHttpActionResult Makes()
{
    using (var db = new eCatEntities())
    {
        var makes = db.VEHICLEs.Select(p => new { TEXT = p.VEHI_MAKE }).Distinct().OrderBy(p => p.TEXT);

        return Json(makes.ToList());
    }
}

[Route("api/Models/{make}")]
[HttpGet]
public IHttpActionResult Models(string make)
{
    using (var db = new eCatEntities())
    {
        var models = db.VEHICLEs.Where(p => p.VEHI_MAKE == make.Replace("|", "/")).Select(p => new { TEXT = p.VEHI_MODEL, p.VEHI_ALPHASORT }).Distinct().OrderBy(p => p.VEHI_ALPHASORT);

        return Json(models.ToList());
    }
}

An example url is:

http://domain:port/api/Makes
http://domain:port/api/Models/Jaguar|Daimler
like image 814
Valeklosse Avatar asked Dec 30 '25 09:12

Valeklosse


1 Answers

The extension point for extending routing is the IHttpRoute (or its default concrete implementation HttpRoute). A handler is a lower-level object that doesn't have the ability to generate outgoing routes (something you are definitely going to need to fix if you are putting / in your route values).

public class ReplacePipeHttpRoute : System.Web.Http.Routing.HttpRoute
{
    public ReplacePipeHttpRoute(string routeTemplate, object defaults)
        : base(routeTemplate, new HttpRouteValueDictionary(defaults))
    { }

    // OPTIONAL: Add additional overloads for constraints, dataTokens, and handler

    // Converts | to / on incoming route
    public override IHttpRouteData GetRouteData(string virtualPathRoot, System.Net.Http.HttpRequestMessage request)
    {
        var routeData = base.GetRouteData(virtualPathRoot, request);

        // RouteData will be null if the URL or constraint didn't match
        if (routeData != null)
        {
            var newValues = new HttpRouteValueDictionary();
            foreach (var r in routeData.Values)
            {
                newValues.Add(r.Key, r.Value.ToString().Replace("|", "/"));
            }
            routeData = new HttpRouteData(this, newValues);
        }

        return routeData;
    }

    // Converts / to | on outgoing route (URLs that are generated from WebApi)
    public override IHttpVirtualPathData GetVirtualPath(System.Net.Http.HttpRequestMessage request, IDictionary<string, object> values)
    {
        var newValues = new Dictionary<string, object>();
        if (values != null)
        {
            foreach (var r in values)
            {
                // Encode pipe as outlined in: http://www.tutorialspoint.com/html/html_url_encoding.htm
                newValues.Add(r.Key, r.Value.ToString().Replace("/", "%7c"));
            }
        }

        return base.GetVirtualPath(request, newValues);
    }
}

Usage

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.Add(
            name: "DefaultApi",
            route: new ReplacePipeHttpRoute(
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional, controller = "Vehicles" })
        );
    }
}

NOTE: You might want to reconsider using a pipe character in your URLs.

  • https://www.seroundtable.com/archives/023052.html
  • https://perishablepress.com/stop-using-unsafe-characters-in-urls/
like image 178
NightOwl888 Avatar answered Jan 01 '26 21:01

NightOwl888