How to write a custom parameter binding to construct an object either from body or from Uri's query

Mike has wrote this really good blog on writing a custom HttpParameterBinding at

https://blogs.msdn.com/b/jmstall/archive/2012/05/11/webapi-parameter-binding-under-the-hood.aspx

My blog just want to add a little bit complex example where you might want to have an action which looks like the following:

Code Snippet

  1. public class HomeController : ApiController
  2. {
  3.     [HttpGet]
  4.     [HttpPost]
  5.     public HttpResponseMessage BindCustomComplexTypeFromUriOrBody(TestItem item)
  6.     {
  7.         return new HttpResponseMessage
  8.         {
  9.             Content = new StringContent(String.Format("BindCustomComplexType item.Name = {0}.", item.Name))
  10.         };
  11.     }
  12. }
  13.  
  14. public class TestItem
  15. {
  16.     public string Name { get; set; }
  17. }

Then the TestItem object can sometimes come from query like /?Name=Hongmei or coming from body. {Name:Hongmei}. It will be easy to write a custom parameter binding to do that.

Step 1: Write a custom FromUriOrFromBodyParameterBinding

Code Snippet

  1. public class FromUriOrBodyParameterBinding : HttpParameterBinding
  2.     {
  3.         HttpParameterBinding _defaultUriBinding;
  4.         HttpParameterBinding _defaultFormatterBinding;
  5.  
  6.         public FromUriOrBodyParameterBinding(HttpParameterDescriptor desc)
  7.             : base(desc)
  8.         {
  9.             _defaultUriBinding = new FromUriAttribute().GetBinding(desc);
  10.             _defaultFormatterBinding = new FromBodyAttribute().GetBinding(desc);
  11.         }
  12.  
  13.         public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken)
  14.         {
  15.             if (actionContext.Request.Content != null )
  16.             {
  17.                 // we have something from the body, try that first
  18.                 return _defaultFormatterBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken);
  19.             }
  20.             else
  21.             {
  22.                 // we need to read things from uri
  23.                 return _defaultUriBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken);
  24.             }
  25.         }
  26.  
  27.     }

 

Step 2: Wire the FromUriOrFromBodyParameterBinding to the configuration

Code Snippet

  1. public static HttpParameterBinding GetCustomParameterBinding(HttpParameterDescriptor descriptor)
  2.         {
  3.             if ( descriptor.ParameterType == typeof(TestItem))
  4.             {
  5.                 return new FromUriOrBodyParameterBinding(descriptor);
  6.             }
  7.  
  8.             // any other types, let the default parameter binding handle
  9.             return null;
  10.         }

Code Snippet

  1. config.ParameterBindingRules.Insert(0, GetCustomParameterBinding);