asp.netasp.net-coreasp.net-core-2.0asp.net-core-webapicustom-model-binder

Custom model binder not firing for a single property in ASP.NET Core 2


I already have tried this, but I don't think it's my case. This doesn't work neither.

I'm using ASP.NET Core 2 Web API. I just created a dummy model binder (what it does doesn't matter for now):

public class SanitizeModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException(nameof(bindingContext));
        }

        var modelName = bindingContext.ModelName;

        return Task.CompletedTask;
    }
}

Now, I have a model. This one:

public class UserRegistrationInfo
{
    public string Email { get; set; }

    [ModelBinder(BinderType = typeof(SanitizeModelBinder))]
    public string Password { get; set; }
}

And an action method:

[AllowAnonymous]
[HttpPost("register")]
public async Task<IActionResult> RegisterAsync([FromBody] UserRegistrationInfo registrationInfo)
{
    var validationResult = validateEmailPassword(registrationInfo.Email, registrationInfo.Password);
    if (validationResult != null) 
    {
        return validationResult;
    }

    var user = await _authenticationService.RegisterAsync(registrationInfo.Email, registrationInfo.Password);

    if (user == null)
    {
        return StatusCode(StatusCodes.Status500InternalServerError, "Couldn't save the user.");
    }
    else
    {
        return Ok(user);
    }
}

If I make a post request from the client, my custom model binder isn't fired and the execution continues in the action method.

Things I have tried:

Applying the ModelBinder attribute to the whole model object:

[ModelBinder(BinderType = typeof(SanitizeModelBinder))]
public class UserRegistrationInfo
{
    public string Email { get; set; }
    public string Password { get; set; }
}

This works, but for the whole object, and I don't want that. I want the default model binder to do its job and then, apply my custom model binder to certain properties only.

I read here that it's the FromBody 's fault, so I removed it from the action method. It doesn't work neither.

I tried to change the attribute ModelBinder for BindProperty here:

public class UserRegistrationInfo
{
    public string Email { get; set; }

    [BindProperty(BinderType = typeof(SanitizeModelBinder))]
    public string Password { get; set; }
}

But it doesn't work.

It's quite disapointing that something that should be simple is turning to be quite cumbersome, and the information scattered on several blogs and github issues isn't helping at all. So please, if you can help me, it would be really appreciated.


Solution

  • For ModelBinder, you need to use application/x-www-form-urlencoded at client side and [FromForm] at server side.

    For ApiController, its default binding is JsonConverter.

    Follow steps below:

    1. Change action

      [AllowAnonymous]
      [HttpPost("register")]
      public async Task<IActionResult> RegisterAsync([FromForm]UserRegistrationInfo registrationInfo)
      {
          return Ok(registrationInfo);
      }
      
    2. Angular

      post(url: string, model: any): Observable <any> {
          let formData: FormData = new FormData(); 
          formData.append('id', model.id); 
          formData.append('applicationName', model.applicationName); 
          return this._http.post(url, formData)
              .map((response: Response) => {
                  return response;
              }).catch(this.handleError); 
      }
      

    For using json with custom binding, you could custom formatters, and refer Custom formatters in ASP.NET Core Web API