Custom Model Binding In ASP.NET Core MVC Pattern

Introduction

In the MVC pattern, Model binding maps the HTTP request data to the parameters of a Controllers action method. The parameter can be of a simple type like integers, strings, double etc. or they may be complex types. MVC then binds the request data to the action parameter by using the parameter name.

Model binder provides a mapping between the request data and the application model. The default model binder which is provided by ASP.NET Core MVC supports most of the common data types and would also meet most of our needs. The built-in model binding functionality can be extended by implementing a custom model binder which transform the input prior to binding it to a model.

Solution

For creating a custom model binder class, we have to inherit from IModelBinder interface. This interface has an async method named "BindModelAsync" and it has a parameter of type ModelBindingContext. The ModelBindingContext class provides the context that model on which the binder acts.

1. Using Microsoft.AspNetCore.Mvc.ModelBinding; 2. using System; 3. using System.Threading.Tasks; 4. 5. namespace ModelBinder.ModelBinder 6. { 7. public class CustomModelBinder : IModelBinder 8. { 9. public Task BindModelAsync(ModelBindingContext bindingContext) 10. { 11. throw new NotImplementedException(); 12. } 13. } 14. }

Example

Let us implement a custom model binder that can convert incoming request data which is passed as query string to a user defined class. The request data is the one which contains all model properties separated with pipe (|) and even our custom model binder and it will separate the data and assign them to the repspective model property.

Steps involved in creating a custom model binder.

First step

1. namespace ModelBinder 2. { 3. using Microsoft.AspNetCore.Mvc.ModelBinding; 4. using ModelBinder.Model; 5. using System; 6. using System.Threading.Tasks; 7. public class CustomModelBinder : IModelBinder 8. { 9. public Task BindModelAsync(ModelBindingContext bindingContext) 10. { 11. if (bindingContext == null) 12. throw new ArgumentNullException(nameof(bindingContext)); 13. 14. var values = bindingContext.ValueProvider.GetValue("Value"); 15. if (values.Length == 0) 16. return Task.CompletedTask; 17. 18. var splitData = values.FirstValue.Split(new char[] { '|' }); 19. if (splitData.Length >= 2) 20. { 21. var result = new User 22. { 23. Id = Convert.ToInt32(splitData[0]), 24. Name = splitData[1] 25. }; 26. bindingContext.Result = ModelBindingResult.Success(result); 27. } 28. 29. return Task.CompletedTask; 30. } 31. } 32. }

Once the model is created using the request data, we then have to assign this model to Result property of the binding context using ModelBindingResult.Success method. This method represents model binding operation was successful. Same as the Success Method, It has also a method name “Failed” it represent a fail model binding operation.

Now, the next step is to register a Model binder. We have two ways to register Model binder:

  1. Using the ModelBinder attribute
  2. By defining model binder provider and then register it in startup class

Register custom model binder using ModelBinder Attribute

We can apply a custom model binder using ModelBinder attribute by defining attributes on an action method or model. Whenever we are using this method that is applying attribute on action method, we need to define this attribute on every action method which we want use this custom binding on. Also, we can apply this attribute on model it-self.

Applying the ModelBinder Attribute on a Model

1. namespace ModelBinder.Model 2. { 3. using Microsoft.AspNetCore.Mvc; 4. 5. [ModelBinder(BinderType = typeof(CustomModelBinder))] 6. public class User 7. { 8. public int Id { get; set; } 9. public string Address { get; set; } 10. public string Name { get; set; } 11. 12. } 13. }

Applying ModelBinding Attribute on Action method

1. [HttpGet] 2. [Route("test")] 3. public IActionResult Index([ModelBinder(BinderType = typeof(CustomModelBinder))]User u) 4. { 5. return View(); 6. }

Register custom Model binder in startup class

Also, we can register our custom model binder in a startup class which will then be available for all action methods. To register a custom model binder, we have to create a binder provider. This model binder provider class implements an interface called IModelBinderProvider interface. The built-in model binders, it should be noted that, have also have their own model binder providers. We can also specify the type of arguments model binder produces, not the input of our model binder. In following example, provider works just with "CustomModelBinder".

Custom Model binder provider

1. namespace ModelBinder 2. { 3. using Microsoft.AspNetCore.Mvc.ModelBinding; 4. using ModelBinder.Model; 5. 6. public class CustomModelBinderProvider : IModelBinderProvider 7. { 8. public IModelBinder GetBinder(ModelBinderProviderContext context) 9. { 10. if (context.Metadata.ModelType == typeof(User)) 11. return new CustomModelBinder(); 12. 13. return null; 14. } 15. } 16. }

Now, we have to add this provider to MVC model binder provider collection.Then we can add custom model binder provider to MVC model binder collection. i.e. in the ConfigureServices methods of the Startup class.

1. public void ConfigureServices(IServiceCollection services) 2. { 3. // Add framework services. 4. services.AddMvc( 5. config => config.ModelBinderProviders.Insert(0, new CustomModelBinderProvider()) 6. ); 7. }

Output

custom-model

In the above example, we are receiving the required data from the request (query-string). In the same way, we can also get the data from request body. With the post method, we need to post the data within request body. In the following example, I have read the request body data and converted it in to required form-data.

Model Binder

1. namespace ModelBinder 2. { 3. using Microsoft.AspNetCore.Mvc.ModelBinding; 4. using ModelBinder.Model; 5. using Newtonsoft.Json.Linq; 6. using System; 7. using System.IO; 8. using System.Threading.Tasks; 9. 10. public class CustomModelBinder1 : IModelBinder 11. { 12. public Task BindModelAsync(ModelBindingContext bindingContext) 13. { 14. if (bindingContext == null) 15. throw new ArgumentNullException(nameof(bindingContext)); 16. 17. string valueFromBody = string.Empty; 18. 19. using (var sr = new StreamReader(bindingContext.HttpContext.Request.Body)) 20. { 21. valueFromBody = sr.ReadToEnd(); 22. } 23. 24. 25. 26. 27. if (string.IsNullOrEmpty(valueFromBody)) 28. { 29. return Task.CompletedTask; 30. } 31. 32. string values = Convert.ToString(((JValue)JObject.Parse(valueFromBody)["value"]).Value); 33. 34. var splitData = values.Split(new char[] { '|' }); 35. if (splitData.Length >= 2) 36. { 37. var result = new User1 38. { 39. Id = Convert.ToInt32(splitData[0]), 40. Name = splitData[1] 41. }; 42. bindingContext.Result = ModelBindingResult.Success(result); 43. } 44. 45. return Task.CompletedTask; 46. } 47. } 48. }

Output

custom-model

Impact

With Customer binders, we can get data whose properties are not primitive. This gives us a better control of the data and thus helps us in some scenarios.

Conclusion

ASP.NET Core has many built-in model binders and their providers that meet our most all needs. But custom model binder provides a way to bind our data which is in specific format to our model classes or action parameter.

.Net developers India have shared this concept to help the global asp.net developers community in understanding the concept of Custom Model Binding In ASP.NET Core MVC pattern and its use in MVC based application development.