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.
How could I fix my model binding?