7

I try to apply back end validation whenever empty or invalid values are send to ASP.NET Core Web API endpoint, but I can't figure out how to handle model binding failure errors.

Getting this error probably from ModelState when submitting invalid values: totalPrice: ["Could not convert string to decimal: . Path 'totalPrice', line 1, position 71."] 0: "Could not convert string to decimal: . Path 'totalPrice', line 1, position 71." It looks like model binding is failing and error is displayed directly to the client.

I have pretty simple controller decorated with ApiController attribute.

[ApiController]
public class ProductsController
{
    [HttpPost]
    public IActionResult Post([FromBody]CreateProductDto model)
    {    
        model.Id = await service.CreateProduct(model);

        return CreatedAtRoute(
            routeName: "GetProduct", 
            routeValues: new { id = model.Id }, 
            value: model
        );
    }
}

and my DTO model

public class CreateProductDto
{
    [Required(ErrorMessage = "Invalid value")]
    public decimal totalPrice { get; set;}

    public int count { get; set; }
}

Is there a way to customize the text from model binding errors? I would like to prevent sensitive info to be send and provide friendly feedback to the client?

worldwildwebdev
  • 374
  • 4
  • 17

1 Answers1

4

You can customize your error message from Startup class in ConfigureServices method. You can see details Microsoft document.

Here is an example -

services.AddMvc(options =>
            {
                var iStrFactory = services.BuildServiceProvider().GetService<IStringLocalizerFactory>();
                var L = iStrFactory.Create("ModelBindingMessages", "WebUI"); // Resource file location 
                options.ModelBindingMessageProvider.SetValueIsInvalidAccessor((x) => L["The value '{0}' is invalid."]);

                options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor((x) => L["The field {0} must be a number."]);
                options.ModelBindingMessageProvider.SetMissingBindRequiredValueAccessor((x) => L["A value for the '{0}' property was not provided.", x]);
                options.ModelBindingMessageProvider.SetAttemptedValueIsInvalidAccessor((x, y) => L["The value '{0}' is not valid for {1}.", x, y]);
                options.ModelBindingMessageProvider.SetMissingKeyOrValueAccessor(() => L["A value is required."]);
                options.ModelBindingMessageProvider.SetUnknownValueIsInvalidAccessor((x) => L["The supplied value is invalid for {0}.", x]);
                options.ModelBindingMessageProvider.SetValueMustBeANumberAccessor((x) => L["Null value is invalid.", x]);
            });

You can read this blog.

Mofaggol Hoshen
  • 686
  • 1
  • 7
  • 20
  • I really hoped that would do the trick but it doesn't :/ Tried all the possible properties of ModelBindingMessageProviders. It was very elegant way for handling those errors. – worldwildwebdev Jun 23 '19 at 15:10
  • You can validate your model using IValidatableObject and you can provide your custom error message also. Give a try this, https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2#ivalidatableobject and https://stackoverflow.com/questions/56588900/how-to-validate-uploaded-file-in-asp-net-core/56600245#56600245 – Mofaggol Hoshen Jun 23 '19 at 15:45
  • You can have a look on this also https://stackoverflow.com/questions/3400542/how-do-i-use-ivalidatableobject – Mofaggol Hoshen Jun 23 '19 at 15:52
  • Just tried that and debugged it, but it looks like the error is thrown before ValidationContext and it's somewhere from Newtonsoft.JSON serializer when the model gets bind. – worldwildwebdev Jun 23 '19 at 17:14
  • You can see the code in my question. I'm just trying to prevent whitespaced string to be parsed to decimal and the binding is exploding with the error. – worldwildwebdev Jun 24 '19 at 09:44
  • Validation can be using ValidationAttribute also and you can provide custom validation message. https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2#custom-attributes and https://stackoverflow.com/questions/11959431/custom-validation-attribute-that-compares-the-value-of-my-property-with-another. – Mofaggol Hoshen Jun 25 '19 at 07:08
  • As I said, there is no ValidationContext as parsing JSON and ModelBinding fails before that. The invalid request I'm trying to avoid doesn't even get to the attribute. You can't parse whitespaced string to decimal. You can try it out and see. I'm considering opening issue on GitHub. – worldwildwebdev Jun 25 '19 at 09:12
  • @worldwildwebdev Did you find a solution? – Victor Jul 11 '19 at 16:28
  • @Victor not yet. – worldwildwebdev Jul 12 '19 at 10:03
  • 1
    @Victor I've created GitHub issue and the ASP.NET team promised to fix that in the next release - 3.0.0-preview. See https://github.com/aspnet/AspNetCore/issues/12472 – worldwildwebdev Jul 27 '19 at 16:55