The framework uses Model Binders to map the request strings into a complex object, so my guess is that you will need to create a Custom Model Binder. Please refer Custom Model Binding in ASP.Net Core
But before that, an easier way to try would be to try Binder attributes in your models. BindRequired attribute adds a model state error if binding cannot occur. So you can modify your model as :
public class Thing
{
[BindRequired]
public string Description {get;set;}
[BindRequired]
public int Amount {get;set;}
}
If that doesn't work for you, then you can try to create a custom model binder. An example from the article :
[ModelBinder(BinderType = typeof(AuthorEntityBinder))]
public class Author
{
public int Id { get; set; }
public string Name { get; set; }
public string GitHub { get; set; }
public string Twitter { get; set; }
public string BlogUrl { get; set; }
}
public class AuthorEntityBinder : IModelBinder
{
private readonly AppDbContext _db;
public AuthorEntityBinder(AppDbContext db)
{
_db = db;
}
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
var modelName = bindingContext.ModelName;
// Try to fetch the value of the argument by name
var valueProviderResult =
bindingContext.ValueProvider.GetValue(modelName);
if (valueProviderResult == ValueProviderResult.None)
{
return Task.CompletedTask;
}
bindingContext.ModelState.SetModelValue(modelName,
valueProviderResult);
var value = valueProviderResult.FirstValue;
// Check if the argument value is null or empty
if (string.IsNullOrEmpty(value))
{
return Task.CompletedTask;
}
int id = 0;
if (!int.TryParse(value, out id))
{
// Non-integer arguments result in model state errors
bindingContext.ModelState.TryAddModelError(
modelName,
"Author Id must be an integer.");
return Task.CompletedTask;
}
// Model will be null if not found, including for
// out of range id values (0, -3, etc.)
var model = _db.Authors.Find(id);
bindingContext.Result = ModelBindingResult.Success(model);
return Task.CompletedTask;
}
}
You might also want to look at Model Validation
options.InvalidModelStateResponseFactory
. That way, you still don't need to checkModelState.IsValid
all over the place, and you can still return your custom response. – Hebraist