Introduction
The purpose of the article is to overcome such scenario where we want to handle custom data binding in MVC application. We know that data binding and Request/Response processing is one of the key extensible point of MVC Framework.
Custom Formatter
Let’s start with custom formatter. This is very useful when we want to send/get data structure other than JSON or XML. We know that MVC supports both data encoding scheme by default. Probably the other solution to red off the problem is just to prepare XML or JSON format of your data before send to API. But the better solution could be writing your custom formatter and hook up in MVC framework. Say we have some comma separated information which we want to feed to API. In this example, we will see how to process such value using custom formatter. Here is our simplest DTO class.
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
Now we have created CommaFormatter
which is derived from "BufferedMediaTypeFormatter
". The mime type is chosen in constructor.
public class CommaFormatter : BufferedMediaTypeFormatter
{
public CommaFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/colon"));
}
public override bool CanReadType(Type type)
{
if (type == typeof(Person) || type == typeof(List<Person>))
return true;
else
return false;
}
public override bool CanWriteType(Type type)
{
if (type == typeof(Person) || type == typeof(List<Person>))
return true;
else
return false;
}
public override object ReadFromStream(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
{
List<Person> Obj = new List<Person>();
Person p = null;
StreamReader reader = new StreamReader(readStream);
string str = reader.ReadToEnd();
string []propArr = str.Split(',');
foreach (var item in propArr)
{
string[] propAttribute = item.Split('|');
p = new Person();
foreach (var item1 in propAttribute)
{
string[] propAttribute1 = item1.Split('=');
if (propAttribute1[0].ToLower().Trim().Equals("name"))
{
p.Name = propAttribute1[1];
}
if (propAttribute1[0].ToLower().Trim().Equals("surname"))
{
p.Surname = propAttribute1[1];
}
}
Obj.Add(p);
}
return Obj;
}
}
Now it’s time to hook up the formatter in Global page so that it got registered in MVC.
GlobalConfiguration.Configuration.Formatters.Add(new CommaFormatter());
In the Post
method of API we are trying to pass list of "Person"
public class CustomController : ApiController
{
[HttpPost]
public void Post(List<Person> p)
{
}
}
Please make sure that the "Content-Type" is set in request header.
You can observe that the payload is as per our custom data format.
And it’s bound to action method.
Custom model binding
This is another way to bind unformatted data to MVC endpoint. Just think of one scenario where data is coming from a query string. We know that if the key of query string matches with the model property then MVC is intelligent enough to bind by default. But what if the query string does not match with model property? The custom model binding comes into the picture. We took a simple Person model to demonstrate the example.
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
}
Here we have created PersonModelBinder
which is derived from DefaultModelBinder
and overrides the BindModel
method. In this method, we are getting the value of query string from request context and returning as concrete object of person model.
public class PersonModelBinder : DefaultModelBinder
{
public PersonModelBinder()
{
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var request = controllerContext.HttpContext.Request;
string name = request.QueryString["name"];
string surname = request.QueryString["surname"];
return new Person { Name = name, Surname = surname };
}
}
Here is the procedure to hook up the custom formatter in MVC pipeline.
ModelBinders.Binders.Add(typeof(Person), new PersonModelBinder());
To receive data as a parameter we have to use ModelBinder
attribute.
public class CustomController : Controller
{
public void Post([ModelBinder(typeof(PersonModelBinder))] Person p)
{
}
}
We are seeing that the value is binded in action.
Conclusion: In these two examples we have seen how to handle custom data format in MVC. The formatter is useful when you don’t want to convert your data to XML or JSON prior to sending to the API and custom model binder is suitable when the amount of data is small and short enough to pass as query string.