Un'altra variante della risposta di @ takepara ma con una diversa svolta:
1) Preferisco il meccanismo di attributo opt-in "StringTrim" (piuttosto che l'esempio "NoTrim" di opt-out di @Anton).
2) È necessaria una chiamata aggiuntiva a SetModelValue per assicurarsi che ModelState sia popolato correttamente e che il modello di convalida / accettazione / rifiuto predefinito possa essere utilizzato normalmente, ad esempio TryUpdateModel (modello) da applicare e ModelState.Clear () per accettare tutte le modifiche.
Inserisci questo nella tua entità / libreria condivisa:
/// <summary>
/// Denotes a data field that should be trimmed during binding, removing any spaces.
/// </summary>
/// <remarks>
/// <para>
/// Support for trimming is implmented in the model binder, as currently
/// Data Annotations provides no mechanism to coerce the value.
/// </para>
/// <para>
/// This attribute does not imply that empty strings should be converted to null.
/// When that is required you must additionally use the <see cref="System.ComponentModel.DataAnnotations.DisplayFormatAttribute.ConvertEmptyStringToNull"/>
/// option to control what happens to empty strings.
/// </para>
/// </remarks>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class StringTrimAttribute : Attribute
{
}
Quindi questo nella tua applicazione / libreria MVC:
/// <summary>
/// MVC model binder which trims string values decorated with the <see cref="StringTrimAttribute"/>.
/// </summary>
public class StringTrimModelBinder : IModelBinder
{
/// <summary>
/// Binds the model, applying trimming when required.
/// </summary>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// Get binding value (return null when not present)
var propertyName = bindingContext.ModelName;
var originalValueResult = bindingContext.ValueProvider.GetValue(propertyName);
if (originalValueResult == null)
return null;
var boundValue = originalValueResult.AttemptedValue;
// Trim when required
if (!String.IsNullOrEmpty(boundValue))
{
// Check for trim attribute
if (bindingContext.ModelMetadata.ContainerType != null)
{
var property = bindingContext.ModelMetadata.ContainerType.GetProperties()
.FirstOrDefault(propertyInfo => propertyInfo.Name == bindingContext.ModelMetadata.PropertyName);
if (property != null && property.GetCustomAttributes(true)
.OfType<StringTrimAttribute>().Any())
{
// Trim when attribute set
boundValue = boundValue.Trim();
}
}
}
// Register updated "attempted" value with the model state
bindingContext.ModelState.SetModelValue(propertyName, new ValueProviderResult(
originalValueResult.RawValue, boundValue, originalValueResult.Culture));
// Return bound value
return boundValue;
}
}
Se non si imposta il valore della proprietà nel raccoglitore, anche quando non si desidera modificare nulla, si bloccherà completamente quella proprietà da ModelState! Questo perché sei registrato come vincolante per tutti i tipi di stringa, quindi sembra (nei miei test) che il raccoglitore predefinito non lo farà per te.