Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asp.Net Core Model binding, how to get empty field to bind as a blank string?

If a form like the one below is submitted and MyField is left blank on the form, then by default Asp.Net Core model binding will place null into the corresponding property on the model as indicated below.

Example Form

 <form asp-controller="SomeController" asp-action="SomeAction">
        <label asp-for="MyField">My Field</label><input asp-for="MyField" type="text" />    
        <button type="submit">Submit</button>
 </form>


Example Model

 public class MyModel{
     public string MyField { get; set; }
 }


Example Action Method

    [HttpPost]
    public IActionResult Post(MyModel m) {
          //m.MyField will be null if the field was left empty
          //but I want it set to a blank string by the model binder
    }

However, since MyField is actually transmitted in the Http Post body I'd prefer that the model binder set the MyField property on the model to a blank string rather than setting it to null. I'd prefer to reserve null for cases where MyField is not transmitted in the Http Post body. How can the model binder be changed to exhibit this behavior?

like image 677
RonC Avatar asked Jun 05 '17 19:06

RonC


Video Answer


1 Answers

Studying the ASP.NET Core code for SimpleTypeModelBinder at https://github.com/aspnet/Mvc/blob/rel/1.1.3/src/Microsoft.AspNetCore.Mvc.Core/ModelBinding/Binders/SimpleTypeModelBinder.cs I could see that there is a ModelMetadata.ConvertEmptyStringToNull setting which is set to true by default that is causing the blank string to be converted to null on data binding. But the property is read only so at first I couldn't figure out how to changes its value.

@rsheptolut's post on this page https://github.com/aspnet/Mvc/issues/4988 led me to a solution.

Solution:

The value has to get set at startup. This can be done via this class:

public class CustomMetadataProvider : IMetadataDetailsProvider, IDisplayMetadataProvider {
    public void CreateDisplayMetadata(DisplayMetadataProviderContext context) {

        if (context.Key.MetadataKind == ModelMetadataKind.Property) {
    
            context.DisplayMetadata.ConvertEmptyStringToNull = false;
        }
    }
}

When it's hooked into MvcOptions in the ConfigureServices method of the startup.cs file like so

services.AddMvc()
       .AddMvcOptions(options => options.ModelMetadataDetailsProviders.Add(new CustomMetadataProvider ())); 

Now site wide, the default for a blank field that is posted back will be for the data binder to set the corresponding model property to a blank string rather than to null. Yea!

like image 143
RonC Avatar answered Oct 22 '22 15:10

RonC