Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create Property Model Binder in Web API

I have created ModelBinder for a complex class. I would like to reuse this ModelBinder on a property. So is it possible to use ModelBinder on a property in Web API. I'm searching for an sample implementation which provides property binder like in an MVC. Below is the reference link which i came across but those implementation is for MVC. Any help appreciated.

Reference Link 1

Reference Link 2

like image 391
mehul9595 Avatar asked May 23 '14 07:05

mehul9595


People also ask

What is model binder in Web API?

Model Binding is the most powerful mechanism in Web API 2. It enables the response to receive data as per requester choice. i.e. it may be from the URL either form of Query String or Route data OR even from Request Body. It's just the requester has to decorate the action method with [FromUri] and [FromBody] as desired.

What is difference between FromQuery and FromBody?

[FromQuery] - Gets values from the query string. [FromRoute] - Gets values from route data. [FromForm] - Gets values from posted form fields. [FromBody] - Gets values from the request body.


1 Answers

I had the same goal, however didn't find the appropriate solution to parse the exact property using binder, and not the whole model. However, there is a solution which more or less suites me - it acts pretty in same way, but requires to mark the model with attribute. I know I'm a bit late, but maybe will help someone with the same issue.

The solution is to use the custom TypeConverter for the required type. First of all, decide how you would like to parse your model. In my example, I need to parse search condition somehow, so my complex model is:

[TypeConverter(typeof(OrderModelUriConverter))] // my custom type converter, which is described below
public class OrderParameter
{
    public string OrderBy { get; set; }
    public int OrderDirection { get; set; }
}

public class ArticlesSearchModel
{
    public OrderParameter Order { get; set; }
    // whatever else
}

Then decide how would you like to parse the input. In my case I simply split the values by comma.

public class OrderParameterUriParser
{
    public bool TryParse(string input, out OrderParameter result)
    {
        result = null;
        if (string.IsNullOrWhiteSpace(input))
        {
            return false;
        }
        var parts = input.Split(',');
        result = new OrderParameter();
        result.OrderBy = parts[0];
        int orderDirection;
        if (parts.Length > 1 && int.TryParse(parts[1], out orderDirection))
        {
            result.OrderDirection = orderDirection;
        }
        else
        {
            result.OrderDirection = 0;
        }
        return true;
    }
}

Then create a converter which will take the part of query and convert it to your model using the above rules.

public class OrderModelUriConverter : TypeConverter
{
    private static readonly Type StringType = typeof (string);

    public OrderModelUriConverter()
    {
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == StringType)
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        var str = value as string;
        if (str != null)
        {
            var parser = new OrderParameterParser()
            OrderParameter orderParameter;
            if (parser.TryParse(str, out orderParameter))
            {
                return orderParameter;
            }
        }
        return base.ConvertFrom(context, culture, value);
    }
}

The usage of the converter is specified in the first sample. Since you are parsing URI, don't forget to add also [FromUri] attribute to your parameter in controller's method.

[HttpGet]
public async Task<HttpResponseMessage> Search([FromUri] ArticlesSearchModel searchModel) // the type converter will be applied only to the property of the types marked with our attribute
{
    // do search
}

Also, you can take a look at this excellent article, which contains similar example and also several other ways of parsing parameters.

like image 147
Eadel Avatar answered Sep 18 '22 17:09

Eadel