I'm trying to do some model binding on simple types with TypeConverter
in ASP.NET Core 2, i.e. converting a string
to my custom type.
If the string has the wrong format, I would like to indicate it, e.g. by throwing an exception:
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string s)
{
var result = Parse(s);
if (!result.Success)
{
throw new ArgumentException("Invalid format", nameof(value), result.Exception);
}
return result.Value;
}
return base.ConvertFrom(context, culture, value);
}
It currently seems like the exception is simply swallowed and ignored, leaving the bound value the default value. The caller of the endpoint is never told that the value is wrong, nor does my code in the controller know that the value was originally invalid (the default value could easily be a valid value).
I wish the conversion to fail hard, if the format is invalid, but how do I do that?
The caller of the endpoint is never told that the value is wrong, nor does my code in the controller know that the value was originally invalid (the default value could easily be a valid value).
All model binding errors are communicated via ControllerBase.ModelState
property accessible in controller actions. ModelState
has IsValid
property set to false
if some error happens during model binding or validation.
It's an intended separation of concerns. Such approach has following advantages comparing to exception bubbling as you want:
ModelState
. With exception approach only the first met error will be communicated.ModelState
you could take appropriate actions on later pipeline stages and decide whether the request still could be processed with encountered errors.ModelState
approach is much more flexible in error handling. You could return appropriate response (e.g. 400 Bad Request
or return HTML view with detailed error description).The most basic approach for handling invalid model state is through thin Action Filter that checks ModelState.IsValid
value:
public class CheckModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Registering the filter in Startup.ConfigureServices():
services.AddMvc(options =>
{
options.Filters.Add(new CheckModelStateAttribute());
});
In the case of model binding error, HTTP error code 400 Bad Request
will be returned to the caller with , controller action will not be invoked.