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);
      }
  }
}
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;
   }
}
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.