Search code examples
c#asp.net-mvcasp.net-core.net-coreasp.net-mvc-routing

Parse to object with a route like ASP.NET MVC routing


In ASP.NET MVC it's possible to define a route like this:

routes.MapRoute("myroute",
    "myroute/{country}/{name}-{type}",
    new { controller = "MyController", action = "Get" });

And that would parse it directly to an object:

public class MyController : Controller
{
   public HttpResponseMessage Get([FromRoute] MyViewModel model)
   {
      //TODO do stuff with model.
   }
}

This is my view model:

public class MyViewModel
{
    public string Name { get; set; }
    public string Type{ get; set; }
}

My question is, can I do the same parsing in a simple console app?

class Program
{
    static void Main(string[] args)
    {
        string route = "myroute/{country}/{name}-{type}";

        string input = "myroute/Denmark/MyName-MyType";

        //TODO Parse input to MyViewModel with route
        MyViewModel result;
    }
}

public class MyViewModel
{
    public string Name { get; set; }
    public string Type { get; set; }
}

There must be some way of doing this, since it's possible for ASP.NET MVC routing.


Solution

  • Parsing and applying the route template is actually pretty simple using Microsoft.AspNetCore.Routing:

    string route = "/myroute/{country}/{name}-{type}";
    string input = "/myroute/Denmark/MyName-MyType";
    
    var routeTemplate = TemplateParser.Parse(route);
    var matcher = new TemplateMatcher(routeTemplate, null);
    var values = new RouteValueDictionary();
    
    if (matcher.TryMatch(input, values))
    {
        foreach (var item in values)
        {
            Console.WriteLine("{0}: {1}", item.Key, item.Value);
        }
    }
    
    country: Denmark
    type: MyType
    name: MyName
    

    However, binding that to an entity would mean that you will have the whole model binding stack which happens to be a “bit” more complex to spin up separately. So instead, I would just recommend you to make this manually using a little bit of reflection:

    public static T BindValues<T>(RouteValueDictionary values)
        where T : new()
    {
        var obj = new T();
        foreach (var prop in typeof(T).GetProperties())
        {
            if (values.ContainsKey(prop.Name))
            {
                prop.SetValue(obj, values[prop.Name]);
            }
        }
        return obj;
    }
    

    And used like this:

    var obj = BindValues<MyViewModel>(values);
    

    While this is obviously a lot less powerful than model binding, it should work fine enough for your use case.