Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 482
amedina Avatar asked Apr 13 '19 19:04

amedina


Video Answer


1 Answers

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

like image 132
Edward Avatar answered Sep 21 '22 17:09

Edward