Last active
August 29, 2015 14:03
-
-
Save chrisdavies/b49d3a702830c4ab6c14 to your computer and use it in GitHub Desktop.
Better URL binding in WebAPI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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