Required and BindRequired Data Annotations

In ASP.NET we have Data Annotations that are attributes that can be placed above properties or classes in order to validate them.

The default annotations are very simple to use because ASP.NET will take care by creating the validation needed for the server and client-side.

The most used data annotation is [Required]. This validation has a downside and I have seen it many times from junior programmers. If you use the validation Required for value types it doesn’t work as expected. An attacker can easily send a model without that value.

public class Employee
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
}

public class EmployeeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public IActionResult Index(Employee e)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        return View();
    }
}

The above validation can be easily be tricked.  Look how I did with Postman.ASP.NET Core Required Attribute

You can see that I didn’t send the Id as a parameter and the Model State is still valid. That’s happen because the property’s type is Integer, which is a value type variable.

A value type variable cannot be null, it always has a default value. In our case, for integer type is 0. So, the required attribute will look at the value which is not null and will validate successfully the property.

If you want to require the user to send a value for the Id property then you should use [BindRequired]. This data annotation will ensure that the user has sent an actual value.

Using nullable types for parameters

An alternative for this approach is to use nullable types. For example, the above class will be changed like this:

public class Employee
{
    [Required]
    public int? Id { get; set; }
    [Required]
    public string Name { get; set; }
}

IBindingMetadataProvider implementation

Also, you can change how the Required attribute works:

public class RequiredBindingProvider : IBindingMetadataProvider
{
    public void CreateBindingMetadata(BindingMetadataProviderContext context)
    {
        if (context.PropertyAttributes?.OfType<RequiredAttribute>().Any() ?? false)
        {
            context.BindingMetadata.IsBindingRequired = true;
        }
    }
}

Then in ConfigureServices:

services.AddControllersWithViews(m =>
{
    m.ModelMetadataDetailsProviders.Add(new RequiredBindingProvider());
});

Using this approach you will also verify for the Required attribute if the value was bound.

In conclusion, checked your application again to see if you use the required attribute on value types and change it in BindRequired or change the way it’s working.

1 thought on “Required and BindRequired Data Annotations”

Leave a Comment