Search code examples
jsonasp.net-core-webapi.net-6.0request.querystring

ASP.NET Core 6 Web API : use queryString on complex json can not bind successfully


When I use the GET API to send a complex JSON format, the backend cannot obtain the data correctly.

This my query string data:

{
    "page": 1,
    "page_size": 10,
    "sortBy": 
    [
        {"key": "reservation_no", "order": "desc"},
        {"key": "pr_no", "order": "desc"},
        {"key": "pr_item", "order": "asc"}
    ]
}

Then it will be converted to this format

?page=1&page_size=11&sortBy[0][key]=reservation_no&sortBy[0][order]=desc&sortBy[1][key]=pr_no&sortBy[1][order]=desc&sortBy[2][key]=pr_item&sortBy[2][order]=asc

My backend can get page and page_size, but sortBy is null.


Solution

  • CustomQueryParametersBinder:

    using complex_json_can_not_bind_successful.Models;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    
    namespace complex_json_can_not_bind_successful
    {
        public class CustomQueryParametersBinder : IModelBinder
        {
            public Task BindModelAsync(ModelBindingContext bindingContext)
            {
                var query = bindingContext.HttpContext.Request.Query;
    
                var pageValue = query["page"].FirstOrDefault();
                var pageSizeValue = query["page_size"].FirstOrDefault();
                var sortByValues = query.Where(q => q.Key.StartsWith("sortBy")).ToDictionary(k => k.Key, v => v.Value.ToString());
    
                var result = new QueryParameters
                {
                    Page = int.TryParse(pageValue, out var page) ? page : 1,
                    PageSize = int.TryParse(pageSizeValue, out var pageSize) ? pageSize : 10,
                    SortBy = sortByValues
                        .GroupBy(kv => int.Parse(kv.Key.Split('[')[1].Split(']')[0]))
                        .Select(g => new SortOption
                        {
                            Key = g.FirstOrDefault(kv => kv.Key.Contains("key")).Value,
                            Order = g.FirstOrDefault(kv => kv.Key.Contains("order")).Value
                        })
                        .ToList()
                };
    
                bindingContext.Result = ModelBindingResult.Success(result);
    
                return Task.CompletedTask;
            }
        }
    }
    

    How to use it:

    [HttpGet("method2")]
    public IActionResult Get2([ModelBinder(BinderType = typeof(CustomQueryParametersBinder))] QueryParameters queryParameters)
    {
        return Ok(queryParameters);
    }
    

    You can use sample code like this:

    enter image description here

    [ApiController]
    [Route("[controller]")]
    public class TestController : Controller
    {
        [HttpGet]
        public IActionResult Get([FromQuery] int page, [FromQuery(Name = "page_size")] int pageSize)
        {
            var sortBy = Request.Query
                .Where(q => q.Key.StartsWith("sortBy"))
                .GroupBy(kv => int.Parse(kv.Key.Split('[')[1].Split(']')[0]))
                .Select(g => new SortOption
                {
                    Key = g.FirstOrDefault(kv => kv.Key.Contains("key")).Value.ToString(),
                    Order = g.FirstOrDefault(kv => kv.Key.Contains("order")).Value.ToString()
                })
                .ToList();
    
            var queryParameters = new QueryParameters
            {
                Page = page,
                PageSize = pageSize,
                SortBy = sortBy
            };
    
            // Your logic here
            return Ok(queryParameters);
        }
    }
    

    Models:

    public class SortOption
    {
        public string Key { get; set; }
        public string Order { get; set; }
    }
    
    public class QueryParameters
    {
        public int Page { get; set; }
        public int PageSize { get; set; }
        public List<SortOption> SortBy { get; set; }
    }