Saturday, October 29, 2016

Custom formatter and data binding in MVC

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 register in MVC. 

GlobalConfiguration.Configuration.Formatters.Add(new CommaFormatter());

In 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 observer that the payload is as per our custom data format.

And it’s banded to action method.

Custom model binding

This is another way to bind unformatted fata to MVC endpoint. Just think one scenario where data is coming from query string. We know, that if key of query string matches with 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 picture. We took simple Person model to demonstrate the example.

    public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
    }

Now, here we have created PersonModelBinder which is derived from DefaultModelBinder and override BindModel method.

In this method we are getting 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 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 this two example 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 send to API and custom model binder is suitable when the amount of data is small and short enough to pass as query string.

No comments:

Post a Comment