Skip to content

Instantly share code, notes, and snippets.

@thefringeninja
Created July 26, 2013 15:44
Show Gist options
  • Save thefringeninja/6089932 to your computer and use it in GitHub Desktop.
Save thefringeninja/6089932 to your computer and use it in GitHub Desktop.
public class CommandBinder : IModelBinder
{
private readonly IEnumerable<ITypeConverter> typeConverters;
private readonly IEnumerable<IBodyDeserializer> bodyDeserializers;
private readonly IFieldNameConverter fieldNameConverter;
private readonly BindingDefaults defaults;
public CommandBinder(
IEnumerable<ITypeConverter> typeConverters, IEnumerable<IBodyDeserializer> bodyDeserializers,
IFieldNameConverter fieldNameConverter, BindingDefaults defaults)
{
this.typeConverters = typeConverters;
this.bodyDeserializers = bodyDeserializers;
this.fieldNameConverter = fieldNameConverter;
this.defaults = defaults;
}
#region IModelBinder Members
public object Bind(
NancyContext context, Type modelType, object instance, BindingConfig configuration,
params string[] blackList)
{
var bindingContext = new BindingContext
{
Configuration = configuration,
Context = context,
DestinationType = modelType,
Model = instance,
ValidModelProperties = new PropertyInfo[0],
RequestData = GetDataFields(context)
};
if (TryBindFromBody(context, bindingContext, out instance))
{
return instance;
}
return BindFromForm(bindingContext);
}
public bool CanBind(Type modelType)
{
return typeof (Command).IsAssignableFrom(modelType);
}
#endregion
private object BindFromForm(BindingContext context)
{
var converters = typeConverters.Union(defaults.DefaultTypeConverters);
var constructor = GetBestConstructor(context);
var parameters = constructor.GetParameters().Select(
parameter =>
{
string value;
if (false == context.RequestData.TryGetValue(parameter.Name, out value))
return null;
return (from converter in converters
where converter.CanConvertTo(parameter.ParameterType, context)
select converter.Convert(value, parameter.ParameterType, context)).FirstOrDefault();
}).ToArray();
return constructor.Invoke(Activator.CreateInstance(context.DestinationType, true), parameters);
}
private static ConstructorInfo GetBestConstructor(BindingContext context)
{
var presentParameterCount = context.RequestData.Count;
var allConstructors = context.DestinationType.GetConstructors();
var candidates = from candidate in allConstructors
let parameters = candidate.GetParameters()
where parameters.Count() == presentParameterCount
&& parameters.All(parameter => context.RequestData.ContainsKey(parameter.Name))
select candidate;
var constructor = candidates.FirstOrDefault() ?? (from candidate in allConstructors
orderby candidate.GetParameters().Count() descending
select candidate).First();
return constructor;
}
private bool TryBindFromBody(NancyContext context, BindingContext bindingContext, out object instance)
{
instance = bindingContext.Model;
string contentType = context.Request.Headers.ContentType;
IBodyDeserializer deserializer = bodyDeserializers.Union(defaults.DefaultBodyDeserializers).FirstOrDefault(
d => d.CanDeserialize(contentType, bindingContext));
if (deserializer == null)
return false;
instance = deserializer.Deserialize(contentType, context.Request.Body, bindingContext);
return true;
}
private IDictionary<string, string> GetDataFields(NancyContext context)
{
var dictionaries = new IDictionary<string, string>[]
{
ConvertDynamicDictionary(context.Request.Form),
ConvertDynamicDictionary(context.Request.Query),
ConvertDynamicDictionary(context.Parameters)
};
return new Dictionary<string, string>(dictionaries.Merge(), StringComparer.InvariantCultureIgnoreCase);
}
private IDictionary<string, string> ConvertDynamicDictionary(DynamicDictionary dictionary)
{
if (dictionary == null)
{
return null;
}
return dictionary.GetDynamicMemberNames().ToDictionary(
memberName => this.fieldNameConverter.Convert(memberName),
memberName => (string)dictionary[memberName]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment