0

Edit: the problem is that url parameters are being sent like: columns[0][name] and Asp.Net is not picking up the [name] property. How do I make Asp.Net catch the name property?

I am using jQuery DataTables 1.10 (new version) and processing server side. I am getting an http request formatted as below:

Request Headers:

Accept:application/json, text/javascript, */*; q=0.01
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,it;q=0.6
Cache-Control:no-cache
Connection:keep-alive
Pragma:no-cache
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36
X-Requested-With:XMLHttpRequest

Variables encoded in the request query string:

draw:16
columns[0][data]:0
columns[0][name]:
columns[0][searchable]:true
columns[0][orderable]:true
columns[0][search][value]:
columns[0][search][regex]:false
columns[1][data]:1
columns[1][name]:
columns[1][searchable]:true
columns[1][orderable]:true
columns[1][search][value]:
columns[1][search][regex]:false
columns[2][data]:2
columns[2][name]:
columns[2][searchable]:true
columns[2][orderable]:true
columns[2][search][value]:
columns[2][search][regex]:false
order[0][column]:0
order[0][dir]:desc
start:0
length:10
search[value]:Log
search[regex]:false

Model binding is successfully parsing out the first level. I can see that there are 3 columns, 1 order, and a search, but these are always null. How should I make my model so that I can bind properly? I have tried a dictionary and an array to no avail.

For reference here is my model that I am trying to bind:

public class DataTableParameterModel
{
        public int draw { get; set; }
        public int start { get; set; }
        public int length { get; set; }
        public DataTablesSearch search { get; set; }
        public DataTablesColumn[] columns { get; set; }
        public DataTablesOrder[] order { get; set; }
        public int folderId { get; set; } 
}

public class DataTablesColumn
{
        public string data { get; set; }
        public string name { get; set; }
        public bool searchable { get; set; }
    public bool orderable { get; set; }
    public DataTablesSearch search { get; set; }
}

2 Answers 2

1

Here, my workaround. Maybe it's not the best, but worked for me.

My model definition for the binding process is the following:

    public class DataTablesSentParameters
{
    public int draw { get; set; }
    public int start { get; set; }
    public int length { get; set; }
    public IDictionary<string, string> search { get; set; }
    public IDictionary<string, string>[] order { get; set; }
    public IDictionary<string, string>[] columns { get; set; }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You can write a custom binder.

Here's a sample of one I wrote for DataTables.Net, which submits GETs in a similar way...

This are my classes

public class DataTableRequest : IDataTableRequest
{
    public int? Draw { get; set; }
    public IEnumerable<Column>? Columns { get; set; }
    public IEnumerable<Order>? Order { get; set; }
    public int? Start { get; set; }
    public int? Length { get; set; }
    public ISearch? Search { get; set; }
    public Dictionary<string, string?>? OtherValues { get; set; }
}

public class Column : IColumn
{
    public string? Data { get; set; }
    public string? Name { get; set; }
    public bool? Searchable { get; set; }
    public bool? Orderable { get; set; }
    public Search? Search { get; set; }
}

public class Search : ISearch
{
    public string? Value { get; set; }
    public bool? Regex { get; set; }
}

public class Order : IOrder
{
    public int? Column { get; set; }
    public string? Dir { get; set; }
    public string? Name { get; set; }
}
// DataTableRequestBinder.cs

using System.Text.RegularExpressions;
using CorreoNet.Web.Models.DataTablesNet;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace CorreoNet.Web.Binders;

public class DataTableRequestBinder : IModelBinder
{
    public async Task BindModelAsync(ModelBindingContext bindingContext)
    {
        await Task.Delay(0);
        
        var request = bindingContext.HttpContext.Request;
        var values = bindingContext.ValueProvider;
        
        // initialize with the simple types
        var model = new DataTableRequest
        {
            Draw = int.TryParse(values.GetValue("draw").FirstValue, out var draw) ? draw : null,
            Start = int.TryParse(values.GetValue("start").FirstValue, out var start) ? start : null,
            Length = int.TryParse(values.GetValue("length").FirstValue, out var length) ? length : null,
            Columns = new List<Column>(),
            OtherValues = new Dictionary<string, string?>(),
        };
        
        // get columns
        if (values.ContainsPrefix("columns"))
        {
            var keys = request.Query.Keys.Where(k => k.StartsWith("columns[")).ToList();
            var colNumbers = keys.Select(k => Regex.Match(k, @"columns\[(\d+)\]").Groups[1].Value).Distinct().ToList();
            var columns = new List<Column>();
            
            foreach (var colNumber in colNumbers)
            {
                var column = new Column
                {
                    Data = values.GetValue($"columns[{colNumber}][data]").FirstValue,
                    Name = values.GetValue($"columns[{colNumber}][name]").FirstValue,
                    Searchable = bool.TryParse(values.GetValue($"columns[{colNumber}][searchable]").FirstValue, out var searchable) ? searchable : null,
                    Orderable = bool.TryParse(values.GetValue($"columns[{colNumber}][orderable]").FirstValue, out var orderable) ? orderable : null,
                    Search = new Search
                    {
                        Value = values.GetValue($"columns[{colNumber}][search][value]").FirstValue,
                        Regex = bool.TryParse(values.GetValue($"columns[{colNumber}][search][regex]").FirstValue, out var searchRegex) ? searchRegex : null,
                    }
                };
                
                columns.Add(column);
            }
            
            model.Columns = columns;
        }

        // get order
        if (values.ContainsPrefix("order"))
        {
            var keys = request.Query.Keys.Where(k => k.StartsWith("order[")).ToList();
            var colNumbers = keys.Select(k => Regex.Match(k, @"order\[(\d+)\]").Groups[1].Value).Distinct().ToList();
            var orders = new List<Order>();
            
            foreach (var colNumber in colNumbers)
            {
                var order = new Order
                {
                    Column = int.TryParse(values.GetValue($"order[{colNumber}][column]").FirstValue, out var column) ? column : null,
                    Dir = values.GetValue($"order[{colNumber}][dir]").FirstValue,
                    Name = values.GetValue($"order[{colNumber}][name]").FirstValue,
                };
                
                orders.Add(order);
            }
            
            model.Order = orders;
        }

        // get search
        if (values.ContainsPrefix("search"))
        {
            model.Search = new Search()
            {
                Value = values.GetValue("search[value]").FirstValue,
                Regex = bool.TryParse(values.GetValue("search[regex]").FirstValue, out var searchRegex) ? searchRegex : null,
            };
        }
        
        
        // get other keys
        var otherKeys = request.Query.Keys.Where(k => 
            !k.StartsWith("columns[") && 
            !k.StartsWith("draw") && 
            !k.StartsWith("order[") && 
            !k.StartsWith("search[") && 
            !k.StartsWith("start") && 
            !k.StartsWith("length")
        ).ToList();

        foreach (var key in otherKeys)
            model.OtherValues.Add(key, values.GetValue(key).FirstValue);       

        // bind!!!
        bindingContext.Result = ModelBindingResult.Success(model);
    }
}

and the provider, which you can add in your program.cs

// DataTableRequestBinderProvider.cs

using CorreoNet.Web.Models.DataTablesNet;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;

namespace CorreoNet.Web.Binders;

public class DataTableRequestBinderProvider: IModelBinderProvider
{
    public IModelBinder? GetBinder(ModelBinderProviderContext context)
    {
        ArgumentNullException.ThrowIfNull(context);

        return (context.Metadata.ModelType == typeof(DataTableRequest)) ? 
            new BinderTypeModelBinder(typeof(DataTableRequestBinder)) : 
            null;
    }
}

Hope this helps. :-)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.