DEV Community

M.Usman Rafiq
M.Usman Rafiq

Posted on • Edited on

How To Bind Model Value FromBody by Custom Binder in Asp.net Core

I encountered a scenario a few days back where I was required to use a custom model binder to transform my received values in a FromBody JSON request. When I implemented the Model Binder It was working fine with FromQuery Attribute but was not working with FromBody, same issue as: https://github.com/aspnet/Mvc/issues/8110.

For in-depth learning, you can also follow my channel on YouTube @usmancode

So after spending some hours on this problem, I came to know that the FromBody attribute will be handled using **BodyModelBinder **class with Formatters and ReaderFactory.

public class MyModelBinder : IModelBinder
{
private BodyModelBinder defaultBinder;
public MyModelBinder(IList formatters, IHttpRequestStreamReaderFactory readerFactory)
{
defaultBinder = new BodyModelBinder(formatters, readerFactory);
}

  public async Task BindModelAsync(ModelBindingContext bindingContext)
  {
      // calling the default body binder
      await defaultBinder.BindModelAsync(bindingContext);
      if (bindingContext.Result.IsModelSet && bindingContext.Result.Model is ResetPasswordModel)
      {
          var inputModel = (ResetPasswordModel)bindingContext.Result.Model;

          // all of your property updates with be listed here
          //
          //
          //

          bindingContext.Result = ModelBindingResult.Success(inputModel);
      }
  }
Enter fullscreen mode Exit fullscreen mode

}
And for this model binder, we will required a model provider which will be like :

public class MyModelBinderProvider : IModelBinderProvider
{
private readonly IList _formatters;
private readonly IHttpRequestStreamReaderFactory _readerFactory;
private BodyModelBinderProvider _defaultProvider;

   public MyModelBinderProvider(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
   {
       _formatters = formatters;
       _readerFactory = readerFactory;
       _defaultProvider = new BodyModelBinderProvider(formatters, readerFactory);
   }

   public IModelBinder GetBinder(ModelBinderProviderContext context)
   {
       IModelBinder modelBinder = _defaultProvider.GetBinder(context);

       // default provider returns null when there is error.So for not null setting our binder
       if (modelBinder != null)
       {
           modelBinder = new ProtectedModelBinder(_formatters, _readerFactory);
       }
       return modelBinder;
   }
Enter fullscreen mode Exit fullscreen mode

}
And we need to register this provide in our startup class inside ConfigureServices method like :

services.AddMvc(config =>
{
var readerFactory = services.BuildServiceProvider().GetRequiredService();
config.ModelBinderProviders.Insert(0, new MyModelBinderProvider(config.InputFormatters, readerFactory));

}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
And at last annotate the model you want to customize the binding of which with a line as below:

[ModelBinder(BinderType = typeof(MyModelBinder))]
public class ResetPasswordModel : UserConfirmationModel
And now if you make a http post api call with content type application/json with your model properties in request body then your custom model binding will work.

You can fork our github repository for all useful and examples code : https://github.com/Usman-uzi/techintalk

For all my blog posts you can visit : https:www.techintalk.com

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.