Skip to content

Instantly share code, notes, and snippets.

@chrisdavies
Last active August 29, 2015 14:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisdavies/b49d3a702830c4ab6c14 to your computer and use it in GitHub Desktop.
Save chrisdavies/b49d3a702830c4ab6c14 to your computer and use it in GitHub Desktop.
Better URL binding in WebAPI
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
public class UrlBindingAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ActionArguments.Count == 0)
{
return;
}
var model = GetOrInitializeModel(actionContext);
BindPropertiesFromUrl(actionContext, model);
}
private static void BindPropertiesFromUrl(HttpActionContext actionContext, object model)
{
var modelType = model.GetType();
var routeParams = actionContext.ControllerContext.RouteData.Values;
var queryString = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
MapUrlParams(model, modelType, routeParams);
MapUrlParams(model, modelType, queryString.AllKeys.Select(k => new KeyValuePair<string, object>(k, queryString[k])));
}
private static void MapUrlParams(object model, Type modelType, IEnumerable<KeyValuePair<string, object>> routeParams)
{
foreach (var nameValue in routeParams.Where(k => !string.IsNullOrEmpty(k.Key) && k.Key != "controller"))
{
var prop = modelType.GetProperty(nameValue.Key, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public);
if (prop != null)
{
if (prop.PropertyType.IsArray)
{
SetArrayProperty(model, prop, nameValue.Value);
}
else
{
SetPlainProperty(model, prop, nameValue.Value);
}
}
}
}
private static void SetPlainProperty(object model, PropertyInfo prop, object val)
{
var descriptor = TypeDescriptor.GetConverter(prop.PropertyType);
if (descriptor.CanConvertFrom(typeof(string)))
{
prop.SetValueFast(model, descriptor.ConvertFromString(null, CultureInfo.InvariantCulture, val as string));
}
}
private static void SetArrayProperty(object model, PropertyInfo prop, object val)
{
var stringVals = val.ToString().Split(',');
var elementType = prop.PropertyType.GetElementType();
var descriptor = TypeDescriptor.GetConverter(elementType);
if (descriptor.CanConvertFrom(typeof(string)))
{
var arr = Array.CreateInstance(elementType, stringVals.Length);
for (var i = 0; i < stringVals.Length; ++i)
{
arr.SetValue(descriptor.ConvertFromString(stringVals[i].Trim()), i);
}
prop.SetValueFast(model, arr);
}
}
private static object GetOrInitializeModel(HttpActionContext actionContext)
{
var model = actionContext.ActionArguments.Values.First();
if (model == null)
{
actionContext.ActionArguments[actionContext.ActionArguments.Keys.First()] = model = Activator.CreateInstance(actionContext.ActionDescriptor.ActionBinding.ParameterBindings[0].Descriptor.ParameterType);
}
return model;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment