0

I'm having some issues with model binding for ASP.NET Core 6 controllers.

My generic request model is like so:

[Serializable]
public class CollectionHttpRequestModel
{
    public int Page { get; set; } = 1;
    public string? BookmarkKey { get; set; }
    public int ItemsPerPage { get; set; } = 20;
    public string? Search { get; set; }

    public List<OrderByHttpRequestModel>? OrderBy { get; set; } = new();
    public List<FilterHttpRequestModel>? Filter { get; set; } = new();
}

[Serializable]
public class OrderByHttpRequestModel
{
    /// <summary>
    /// Object key to order by
    /// </summary>
    public string? Key { get; set; }

    /// <summary>
    /// Direction to order by. ASC or DESC.
    /// </summary>
    public string? Direction { get; set; } = "ASC";
}

[Serializable]
public class FilterHttpRequestModel
{
    /// <summary>
    /// Key to filter
    /// </summary>
    public string? Key { get; set; }

    /// <summary>
    /// Operator to use. E.g. '==','!=', 'IS NOT'
    /// </summary>
    public string? Operator { get; set; }

    /// <summary>
    /// Value to filter on
    /// </summary>
    public object? Value { get; set; }

    /// <summary>
    /// Ors to use.
    /// </summary>
    public List<FilterHttpRequestModel>? Ors { get; set; }

    /// <summary>
    /// Ands to use
    /// </summary>
    public List<FilterHttpRequestModel>? Ands { get; set; }
}

I have some code that works - and fills this object from the query string:

[NonAction]
protected virtual CollectionHttpRequestModel GetCollectionRequestModel(int? maxItemsPerPage = 9999)
{
    var request = HttpContext.Request;

    var defaultItemsPerPage = 20;

    var qPage = request.Query["page"];
    var qBookmarkKey = request.Query["bookmarkKey"];
    var qItemsPerPage = request.Query["itemsPerPage"];
    string qOrderBy = request.Query["orderBy"];
    string? qSearch = request.Query["search"];
    string qFilter = request.Query["filter"];

    if (string.IsNullOrWhiteSpace(qSearch))
    {
        qSearch = null;
    }
    else
    {
        qSearch = qSearch.Trim();

    }

    if (!int.TryParse(qPage.FirstOrDefault(), out var page) || page < 1)
    {
        page = 1;
    }

    if (!int.TryParse(qItemsPerPage.FirstOrDefault(), out var itemsPerPage) || itemsPerPage > maxItemsPerPage)
    {
        itemsPerPage = defaultItemsPerPage;
    }

    var orderBy = new List<OrderByHttpRequestModel>();
    var filter = new List<FilterHttpRequestModel>();

    try
    {
        if (!string.IsNullOrWhiteSpace(qOrderBy))
        {
            orderBy = JsonConvert.DeserializeObject<List<OrderByHttpRequestModel>>(qOrderBy);
        }

        if (!string.IsNullOrWhiteSpace(qFilter))
        {
            filter = JsonConvert.DeserializeObject<List<FilterHttpRequestModel>>(qFilter);
        }
    }
    catch (Exception ex)
    {
        Logger.LogWarning(ex, "Swallowed exception whilst deserializing json objects in " + nameof(GetCollectionRequestModel));
    }

    return new()
    {
        Page = page,
        ItemsPerPage = itemsPerPage,
        Search = qSearch,
        OrderBy = orderBy,
        Filter = filter,
        BookmarkKey = qBookmarkKey
    };
}

This works but requires me to call var requestModel = GetCollectionRequestModel(); in every endpoint (or a base class). It also does not works with overridden models like:

public class HarvestLLTimeSheetRequestModel : CollectionHttpRequestModel
{
    public string? OtherProp { get; set; };
}

What I instead would like to do is use this:

[HttpGet]
public async Task<IActionResult> Get([FromQuery] HarvestLLTimeSheetRequestModel harvestLLTimeSheetRequestModel)
{
   ...
}

Unfortunately this fails - the properties filter and order by are never filled.

enter image description here

How could I fix my model binding?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
sommmen
  • 6,570
  • 2
  • 30
  • 51
  • Why use the *query string* though? Is it a MUST?...I once had the same issue (complex object in query string). I eventually ended up using `[FromBody]` and it worked. – Ergis Mar 16 '22 at 16:36
  • @Ergis i'm not sure GET methods have a body - anyways that does not matter for me the frontend works with query string parameters so i'm stuck with that. – sommmen Mar 17 '22 at 07:25
  • Oh, I missed the [HttpGet]. Anyway, if you can't get around it, maybe try [this suggestion](https://stackoverflow.com/questions/13548640/c-sharp-deserialize-complex-object-from-querystring) ? – Ergis Mar 17 '22 at 09:16

0 Answers0