Search code examples
c#asp.netasp.net-mvcasp.net-mvc-4asp.net-mvc-viewmodel

ASP.Net MVC request ViewModel not parsing


I have a request like this:

filter[logic]:and
filter[filters][0][value]:a
filter[filters][0][operator]:startswith
filter[filters][0][field]:result
filter[filters][0][ignoreCase]:true

I need to receive it on the Controller but I don't know exactly how. I have tried this view model:

{
    public class SearchFilterViewModel
    {
        public string logic { get; set; }
        public List<SearchFilterFiltersViewModel> filters { get; set; }
    }

    public class SearchFilterFiltersViewModel
    {
        public string value { get; set; }
        //public string operator { get; set; }
        public string field { get; set; }
        public bool ignoreCase { get; set; }
    }
}

But the Controller receives it all null. operator property is commented because operator is a reserved keyword, I don't know how to make Asp.Net to use it. And I don't know if this is the cause of the problem.

Note that I can't change the request body pattern because it comes from this Kendo Widget.

This is my Controller(test version):

public ActionResult Text(SearchFilterViewModel filter)
{
    return Json("", JsonRequestBehavior.AllowGet);
}

Solution

  • Here is working solution

    Model:

    public class SearchFilterViewModel
    {
        public string logic { get; set; }
        public List<SearchFilterFiltersViewModel> filter { get; set; }
    }
    
    public class SearchFilterFiltersViewModel
    {
        public string value { get; set; }
        public string oper { get; set; }
        public string field { get; set; }
        public bool ignoreCase { get; set; }
    }
    

    Then you can write custom IValueProvider where you can override usual parsing mechanism like this:

    public class KendoValueProvider : NameValueCollectionValueProvider
    {
        public KendoValueProvider(NameValueCollection originalCollection)
            : base(UpdateCollection(originalCollection), CultureInfo.InvariantCulture)
        {
        }
    
        private static NameValueCollection UpdateCollection(NameValueCollection collection)
        {
            NameValueCollection result = new NameValueCollection();
    
            foreach (string key in collection.Keys)
            {
                // ignore all other request
                if (!key.StartsWith("filter"))
                    return null;
    
                var newKey = key
                    .Replace("[filters]", string.Empty)
                    .Replace("filter[logic]", "logic")
                    .Replace("[value]", ".value")
                    .Replace("[operator]", ".oper")
                    .Replace("[field]", ".field")
                    .Replace("[ignoreCase]", ".ignoreCase");
    
                var value = collection[key];
    
                result.Add(newKey, value);
            }
            return result;
        }
    }
    

    Then you need to write ValueProviderFactory that will register this ValueProvider like this:

    public class KendoValueProviderFactory : ValueProviderFactory
    {
        public override IValueProvider GetValueProvider(ControllerContext controllerContext)
        {
            return new KendoValueProvider(controllerContext.HttpContext.Request.QueryString);
        }
    }
    

    And the last step is just register it in Global.asax file

    ValueProviderFactories.Factories.Add(new KendoValueProviderFactory());
    

    And sample Action

    [HttpGet]
    public ActionResult Index(SearchFilterViewModel model)
    {
          return Json(model, JsonRequestBehavior.AllowGet);
    }