Binding is a process to set values for the parameters when Web API calls a controller action method. In this article, we learn how to map Web API methods with the different types of the parameters and how to customize the binding process.
Web API tries to get the value from the URI. If a parameter type is a primitive type (Bool, int, Double, log, Timespan, DateTime, Guid, String) Web API tries to read the value of the parameter from the body for complex type, using media-type formatter.
Example
Web API tries to get the value from the URI. If a parameter type is a primitive type (Bool, int, Double, log, Timespan, DateTime, Guid, String) Web API tries to read the value of the parameter from the body for complex type, using media-type formatter.
Example
- Public HttpResponseMessage Put(int id, Employee employee)
- {
- …
- …
- }
We can also force Web API to get the parameter value from the request body or request URL by using FromUri and FromBody attribute.
FromUri Attribute
It forces the Web API to read the parameter value from the requested URL. In the following example, I have defined the complex type and forced the Web API to read the value for complex type from the requested parameter.
- public classTestData
- {
- public string Name
- {
- get;
- set;
- }
- public int Id
- {
- get;
- set;
- }
- }
- public HttpResponseMessage Get([FromUri] TestData data)
- {……
- return Request.CreateResponse(HttpStatusCode.OK, true);
- }
Example
URI: http://localhost:24367/api/Employee?Name=Jignesh&Id=10
Output
FromBody Attribute
It forces the Web API to read the parameter value from the requested body. In the following example, I forced the Web API to read the value for simple type from the requested body by using FromBody attribute.
Example
- [HttpPost]
- public HttpResponseMessage Post([FromBody] string name)
- {
- ……
- return Request.CreateResponse(HttpStatusCode.OK, true);
- }
To select the media formatter, Web API uses the content type header. In the above example, content type is set to "application/json" and requested body contains a string value, so it binds to the parameter at Web API. It supports the JSON string, not the JSON object, so only one parameter is allowed to read from the message body.
Type converters
We can force Web API to treat complex types as a simple type (i.e. web API bind the value of parameter from URI) using type converter.
In the following example, I created TestData class, which has ID and Name properties. We also created a type converter that converts the string data to equivalent TestData instance. The class TestData is decorated with a TypeConverter attribute to specify the type of a converter.
Type converter definition
- namespace WebAPITest
- {
- using System;
- using System.ComponentModel;
- using System.Globalization;
- public class TestTypeConverter: TypeConverter
- {
- public override bool CanConvertFrom(ITypeDescriptorContext context, TypesourceType)
- {
- if (sourceType == typeof(string))
- {
- returntrue;
- }
- returnbase.CanConvertFrom(context, sourceType);
- }
- public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
- {
- if (value is string)
- {
- TestData data;
- if (TestData.TryParse((string) value, out data))
- {
- return data;
- }
- }
- return base.ConvertFrom(context, culture, value);
- }
- }
- }
- namespaceWebAPITest
- {
- using System.ComponentModel;
- [TypeConverter(typeof(TestTypeConverter))]
- public class TestData
- {
- public string Name
- {
- get;
- set;
- }
- public int Id
- {
- get;
- set;
- }
- public static bool TryParse(string s, outTestData result)
- {
- result = null;
- var parts = s.Split(',');
- if (parts.Length != 2)
- {
- return false;
- }
- int id;
- string name = parts[1];
- if (int.TryParse(parts[0], out id))
- {
- result = newTestData()
- {
- Id = id, Name = name
- };
- return true;
- }
- return false;
- }
- }
- }
- public HttpResponseMessage Get(TestData data)
- {
- ……
- return Request.CreateResponse(HttpStatusCode.OK, true);
- }
Output
Model Binder
Create custom model binder, which is more flexible compared to using the type converter. We can use HTTP request, action description and raw values from URL.
IModelBinder interface is used to create custom model binder. This interface has only one method called "BindModel".
In the following example, I have created custom model binder for the TestData class. In this model binder, I have read the raw value from route data, split this data and set the data to the class properties. Here, I have fetched the raw data value, using a value provider. Here, I have used a simple conversion for Model Binder, but it is not limited to a simple type.
Model Binder code
- namespace WebAPITest
- {
- using System;
- using System.Web.Http.Controllers;
- using System.Web.Http.ModelBinding;
- using System.Web.Http.ValueProviders;
- public class CustomModelBinder: IModelBinder
- {
- static CustomModelBinder()
- {
- }
- public bool BindModel(HttpActionContextactionContext, ModelBindingContextbindingContext)
- {
- if (bindingContext.ModelType != typeof(TestData))
- {
- return false;
- }
- ValueProviderResult val = bindingContext.ValueProvider.GetValue(
- bindingContext.ModelName);
- if (val == null)
- {
- return false;
- }
- string key = val.RawValue as string;
- if (key == null)
- {
- bindingContext.ModelState.AddModelError(
- bindingContext.ModelName, "Wrong value type");
- returnfalse;
- }
- TestData result = newTestData();
- var data = key.Split(newchar[]
- {
- ','
- });
- if (data.Length > 1)
- {
- result.Id = Convert.ToInt32(data[0]);
- result.Name = data[1];
- bindingContext.Model = result;
- return true;
- }
- bindingContext.ModelState.AddModelError(
- bindingContext.ModelName, "Cannot convert value to TestData");
- return false;
- }
- }
- }
- namespace WebAPITest
- {
- using System.Web.Http;
- using System.Web.Http.ModelBinding;
- using System.Web.Http.ModelBinding.Binders;
- public static class WebApiConfig
- {
- public static void Register(HttpConfigurationconfig)
- {
- var provider = newSimpleModelBinderProvider(
- typeof(TestData), newCustomModelBinder());
- config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
- config.MapHttpAttributeRoutes();
- config.Routes.MapHttpRoute(
- name: "DefaultApi",
- routeTemplate: "api/{controller}/{id}",
- defaults: new
- {
- id = RouteParameter.Optional
- });
- }
- }
- }
Easiest way is to add ModelBinder attribute to the parameter. In following example, I have added ModelBinder attribute to the "data" parameter, so web API can understand how to use custom model binder, while binding the data.
- public HttpResponseMessage Get([ModelBinder(typeof(CustomModelBinder))] TestData data)
- {
- ……
- return Request.CreateResponse(HttpStatusCode.OK, true);
- }
Output
Another way is to add ModelBinder attribute to the type. When we define ModelBinder attribute to the type, Web API uses this model binder for all the parameters of this type.
- [ModelBinder(typeof(CustomModelBinder))]
- public class TestData
- {
- //......
- }
No comments:
Post a Comment