Skip to content

Instantly share code, notes, and snippets.

@ismaelgasparin
Created April 25, 2020 01:51
Show Gist options
  • Save ismaelgasparin/08cdaff62a26c6ae110dd6d6de50d4b0 to your computer and use it in GitHub Desktop.
Save ismaelgasparin/08cdaff62a26c6ae110dd6d6de50d4b0 to your computer and use it in GitHub Desktop.
public class TrimmingModelPropertiesBinder : IModelBinder
{
private static readonly Type[] SimpleTypes =
{
typeof(Enum),
typeof(String),
typeof(Decimal),
typeof(DateTime),
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Guid)
};
private readonly BodyModelBinder _defaultBinder;
public TrimmingModelPropertiesBinder(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
{
_defaultBinder = new BodyModelBinder(formatters, readerFactory);
}
private IEnumerable<PropertyInfo> GetPropertiesFromModel(Type type)
{
return type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead && x.CanWrite);
}
private static bool IsSimpleType(Type type)
{
return
type.IsPrimitive ||
SimpleTypes.Contains(type) ||
Convert.GetTypeCode(type) != TypeCode.Object ||
(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]));
}
private void TrimStringValues(object source, PropertyInfo property)
{
if (source != null)
{
// string property
if (property.PropertyType == typeof(string))
{
var propValue = property.GetValue(source);
property.SetValue(source, propValue?.ToString().Trim());
}
else if (!IsSimpleType(property.PropertyType))
{
if (property.PropertyType.IsGenericType)
{
// list property
if (property.GetValue(source) is IList nestedList)
{
foreach (var item in nestedList)
{
var objectProperties = GetPropertiesFromModel(item.GetType());
foreach (var value in objectProperties)
{
TrimStringValues(item, value);
}
}
}
}
// object property
else
{
var nestedObject = property.GetValue(source);
if (nestedObject != null)
{
var objectProperties = GetPropertiesFromModel(nestedObject.GetType());
foreach (var value in objectProperties)
{
TrimStringValues(nestedObject, value);
}
}
}
}
}
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
// calling the default body binder
await _defaultBinder.BindModelAsync(bindingContext);
if (bindingContext.Result.IsModelSet)
{
var data = bindingContext.Result.Model;
if (data != null)
{
var values = GetPropertiesFromModel(data.GetType());
foreach (var value in values)
{
TrimStringValues(data, value);
}
bindingContext.Result = ModelBindingResult.Success(data);
}
}
}
}
public class TrimmingModelPropertiesBinderProvider : IModelBinderProvider
{
private readonly IList<IInputFormatter> _formatters;
private readonly IHttpRequestStreamReaderFactory _readerFactory;
public TrimmingModelPropertiesBinderProvider(IList<IInputFormatter> formatters, IHttpRequestStreamReaderFactory readerFactory)
{
_formatters = formatters;
_readerFactory = readerFactory;
}
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (context.Metadata.BindingSource?.Id == "Body" &&
context.Metadata.IsComplexType &&
!context.Metadata.ModelType.IsEnumerable())
{
return new TrimmingModelPropertiesBinder(_formatters, _readerFactory);
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment