Meilleur moyen de couper les chaînes après la saisie des données. Devrais-je créer un modèle de classeur personnalisé?

J’utilise ASP.NET MVC et j’aimerais que tous les champs de chaîne entrés par l’utilisateur soient tronqués avant d’être insérés dans la firebase database. Et comme j’ai beaucoup de formulaires de saisie de données, je cherche un moyen élégant de couper toutes les chaînes au lieu de couper explicitement chaque valeur de chaîne fournie par l’utilisateur. Je suis intéressé de savoir comment et quand les gens règlent les cordes.

J’ai pensé peut-être à créer un modèle de classeur personnalisé et à y rogner les valeurs de chaîne … Ainsi, toute ma logique de rognage est contenue dans un seul emplacement. Est-ce une bonne approche? Y at-il des exemples de code qui font cela?

public class TrimModelBinder : DefaultModelBinder { protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value) { if (propertyDescriptor.PropertyType == typeof(ssortingng)) { var ssortingngValue = (ssortingng)value; if (!ssortingng.IsNullOrWhiteSpace(ssortingngValue)) { value = ssortingngValue.Trim(); } else { value = null; } } base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value); } } 

Que diriez-vous de ce code?

 ModelBinders.Binders.DefaultBinder = new TrimModelBinder(); 

Définissez l’événement global.asax Application_Start.

Ceci est la même résolution @takepara mais comme un IModelBinder au lieu de DefaultModelBinder de sorte que l’ajout du modelbinder dans global.asax passe par

 ModelBinders.Binders.Add(typeof(ssortingng),new TrimModelBinder()); 

La classe:

 public class TrimModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueResult== null || valueResult.AttemptedValue==null) return null; else if (valueResult.AttemptedValue == ssortingng.Empty) return ssortingng.Empty; return valueResult.AttemptedValue.Trim(); } } 

basé sur @haacked post: http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx

Une amélioration de la réponse à @takepara.

Certains étaient en projet:

 public class NoTrimAtsortingbute : Atsortingbute { } 

Dans le changement de classe TrimModelBinder

 if (propertyDescriptor.PropertyType == typeof(ssortingng)) 

à

 if (propertyDescriptor.PropertyType == typeof(ssortingng) && !propertyDescriptor.Atsortingbutes.Cast().Any(a => a.GetType() == typeof(NoTrimAtsortingbute))) 

et vous pouvez marquer les propriétés à exclure du découpage avec l’atsortingbut [NoTrim].

Avec les améliorations de C # 6, vous pouvez désormais écrire un modèle très compact qui va couper toutes les entrées de chaîne:

 public class TrimSsortingngModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); var attemptedValue = value?.AttemptedValue; return ssortingng.IsNullOrWhiteSpace(attemptedValue) ? attemptedValue : attemptedValue.Trim(); } } 

Vous devez inclure cette ligne quelque part dans Application_Start() dans votre Global.asax.cs pour utiliser le modèle de liant lors de la liaison de ssortingng s:

 ModelBinders.Binders.Add(typeof(ssortingng), new TrimSsortingngModelBinder()); 

Je trouve qu’il est préférable d’utiliser un tel modèle plutôt que de remplacer le modèle par défaut, car il sera utilisé chaque fois que vous liez une ssortingng , que ce soit directement en tant qu’argument de méthode ou en tant que propriété dans une classe de modèle. Toutefois, si vous remplacez le classeur de modèle par défaut, comme le suggèrent d’autres réponses, cela ne fonctionnera que lors de la liaison de propriétés sur des modèles, et non lorsque vous utilisez une ssortingng comme argument d’une méthode d’action.

Edit: un commentateur s’est interrogé sur le traitement de la situation lorsqu’un champ ne doit pas être validé. Ma réponse initiale a été réduite pour traiter uniquement de la question posée par l’OP, mais pour ceux qui sont intéressés, vous pouvez gérer la validation en utilisant le classeur étendu suivant:

 public class TrimSsortingngModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled; var unvalidatedValueProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider; var value = unvalidatedValueProvider == null ? bindingContext.ValueProvider.GetValue(bindingContext.ModelName) : unvalidatedValueProvider.GetValue(bindingContext.ModelName, !shouldPerformRequestValidation); var attemptedValue = value?.AttemptedValue; return ssortingng.IsNullOrWhiteSpace(attemptedValue) ? attemptedValue : attemptedValue.Trim(); } } 

Une autre variante de la réponse de @ takepara mais avec une tournure différente:

1) Je préfère le mécanisme d’atsortingbut optionnel “SsortingngTrim” (plutôt que l’exemple “NoTrim” de @Anton).

2) Un appel supplémentaire à SetModelValue est nécessaire pour s’assurer que le ModelState est correctement rempli et que le modèle de validation / acceptation / rejet par défaut peut être utilisé normalement, par exemple TryUpdateModel (model) à appliquer et ModelState.Clear () pour accepter toutes les modifications.

Mettez ceci dans votre entité / bibliothèque partagée:

 ///  /// Denotes a data field that should be sortingmmed during binding, removing any spaces. ///  ///  ///  /// Support for sortingmming is implmented in the model binder, as currently /// Data Annotations provides no mechanism to coerce the value. ///  ///  /// This atsortingbute does not imply that empty ssortingngs should be converted to null. /// When that is required you must additionally use the  /// option to control what happens to empty ssortingngs. ///  ///  [AtsortingbuteUsage(AtsortingbuteTargets.Property | AtsortingbuteTargets.Field, AllowMultiple = false)] public class SsortingngTrimAtsortingbute : Atsortingbute { } 

Ensuite, ceci dans votre application / bibliothèque MVC:

 ///  /// MVC model binder which sortingms ssortingng values decorated with the . ///  public class SsortingngTrimModelBinder : IModelBinder { ///  /// Binds the model, applying sortingmming when required. ///  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 (!Ssortingng.IsNullOrEmpty(boundValue)) { // Check for sortingm atsortingbute if (bindingContext.ModelMetadata.ContainerType != null) { var property = bindingContext.ModelMetadata.ContainerType.GetProperties() .FirstOrDefault(propertyInfo => propertyInfo.Name == bindingContext.ModelMetadata.PropertyName); if (property != null && property.GetCustomAtsortingbutes(true) .OfType().Any()) { // Trim when atsortingbute 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; } } 

Si vous ne définissez pas la valeur de propriété dans le classeur, même si vous ne voulez rien changer, vous bloquerez complètement cette propriété de ModelState! C’est parce que vous êtes enregistré en tant que liaison de tous les types de chaînes, il apparaît donc (dans mes tests) que le classeur par défaut ne le fera pas pour vous.

Informations supplémentaires pour ceux qui cherchent comment faire dans ASP.NET Core 1.0. La logique a beaucoup changé.

J’ai écrit un blog sur la façon de le faire , il explique les choses un peu plus en détail

Donc, la solution ASP.NET Core 1.0:

Reliure modèle pour effectuer la coupe réelle

 public class TrimmingModelBinder : ComplexTypeModelBinder { public TrimmingModelBinder(IDictionary propertyBinders) : base(propertyBinders) { } protected override void SetProperty(ModelBindingContext bindingContext, ssortingng modelName, ModelMetadata propertyMetadata, ModelBindingResult result) { if(result.Model is ssortingng) { ssortingng resultStr = (result.Model as ssortingng).Trim(); result = ModelBindingResult.Success(resultStr); } base.SetProperty(bindingContext, modelName, propertyMetadata, result); } } 

Aussi, vous avez besoin de Model Binder Provider dans la dernière version, cela indique que ce classeur doit être utilisé pour ce modèle

 public class TrimmingModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) { var propertyBinders = new Dictionary(); foreach (var property in context.Metadata.Properties) { propertyBinders.Add(property, context.CreateBinder(property)); } return new TrimmingModelBinder(propertyBinders); } return null; } } 

Ensuite, il doit être enregistré dans Startup.cs

  services.AddMvc().AddMvcOptions(options => { options.ModelBinderProviders.Insert(0, new TrimmingModelBinderProvider()); }); 

Dans ASP.Net Core 2, cela a fonctionné pour moi. J’utilise l’atsortingbut [FromBody] dans mes contrôleurs et entrée JSON. Pour remplacer la gestion des chaînes dans la désérialisation JSON, j’ai enregistré mon propre JsonConverter:

 services.AddMvcCore() .AddJsonOptions(options => { options.SerializerSettings.Converters.Insert(0, new TrimmingSsortingngConverter()); }) 

Et voici le convertisseur:

 public class TrimmingSsortingngConverter : JsonConverter { public override bool CanRead => true; public override bool CanWrite => false; public override bool CanConvert(Type objectType) => objectType == typeof(ssortingng); public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { if (reader.Value is ssortingng value) { return value.Trim(); } return reader.Value; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } } 

En lisant les excellentes réponses et commentaires ci-dessus, et en devenant de plus en plus confus, j’ai soudainement pensé, hé, je me demande s’il existe une solution jQuery. Donc, pour ceux qui, comme moi, trouvent que ModelBinders est un peu déroutant, je propose l’extrait de code jQuery suivant qui coupe les champs d’entrée avant l’envoi du formulaire.

  $('form').submit(function () { $(this).find('input:text').each(function () { $(this).val($.sortingm($(this).val())); }) }); 

En cas de MVC Core

Classeur:

 using Microsoft.AspNetCore.Mvc.ModelBinding; using System; using System.Threading.Tasks; public class TrimmingModelBinder : IModelBinder { private readonly IModelBinder FallbackBinder; public TrimmingModelBinder(IModelBinder fallbackBinder) { FallbackBinder = fallbackBinder ?? throw new ArgumentNullException(nameof(fallbackBinder)); } public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (valueProviderResult != null && valueProviderResult.FirstValue is ssortingng str && !ssortingng.IsNullOrEmpty(str)) { bindingContext.Result = ModelBindingResult.Success(str.Trim()); return Task.CompletedTask; } return FallbackBinder.BindModelAsync(bindingContext); } } 

Fournisseur:

 using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; using System; public class TrimmingModelBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!context.Metadata.IsComplexType && context.Metadata.ModelType == typeof(ssortingng)) { return new TrimmingModelBinder(new SimpleTypeModelBinder(context.Metadata.ModelType)); } return null; } } 

Fonction d’enregistrement:

  public static void AddSsortingngTrimmingProvider(this MvcOptions option) { var binderToFind = option.ModelBinderProviders .FirstOrDefault(x => x.GetType() == typeof(SimpleTypeModelBinderProvider)); if (binderToFind == null) { return; } var index = option.ModelBinderProviders.IndexOf(binderToFind); option.ModelBinderProviders.Insert(index, new TrimmingModelBinderProvider()); } 

Registre:

 service.AddMvc(option => option.AddSsortingngTrimmingProvider()) 

Je ne suis pas d’accord avec la solution. Vous devez remplacer GetPropertyValue car les données de SetProperty peuvent également être remplies par ModelState. Pour intercepter les données brutes des éléments d’entrée, écrivez ceci:

  public class CustomModelBinder : System.Web.Mvc.DefaultModelBinder { protected override object GetPropertyValue(System.Web.Mvc.ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, System.Web.Mvc.IModelBinder propertyBinder) { object value = base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder); ssortingng retval = value as ssortingng; return ssortingng.IsNullOrWhiteSpace(retval) ? value : retval.Trim(); } } 

Filtrer par propertyDescriptor PropertyType si vous n’êtes vraiment intéressé que par les valeurs de chaîne mais cela ne devrait pas avoir d’importance car tout ce qui entre est essentiellement une chaîne.

Pour ASP.NET Core , remplacez le ComplexTypeModelBinderProvider par un fournisseur qui ajuste les chaînes.

Dans votre méthode ConfigureServices code de démarrage, ajoutez ceci:

 services.AddMvc() .AddMvcOptions(s => { s.ModelBinderProviders[s.ModelBinderProviders.TakeWhile(p => !(p is ComplexTypeModelBinderProvider)).Count()] = new TrimmingModelBinderProvider(); }) 

Définissez TrimmingModelBinderProvider comme ceci:

 ///  /// Used in place of  to sortingm beginning and ending whitespace from user input. ///  class TrimmingModelBinderProvider : IModelBinderProvider { class TrimmingModelBinder : ComplexTypeModelBinder { public TrimmingModelBinder(IDictionary propertyBinders) : base(propertyBinders) { } protected override void SetProperty(ModelBindingContext bindingContext, ssortingng modelName, ModelMetadata propertyMetadata, ModelBindingResult result) { var value = result.Model as ssortingng; if (value != null) result = ModelBindingResult.Success(value.Trim()); base.SetProperty(bindingContext, modelName, propertyMetadata, result); } } public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) { var propertyBinders = new Dictionary(); for (var i = 0; i < context.Metadata.Properties.Count; i++) { var property = context.Metadata.Properties[i]; propertyBinders.Add(property, context.CreateBinder(property)); } return new TrimmingModelBinder(propertyBinders); } return null; } } 

La partie laide de ceci est le copier / coller de la logique GetBinder , mais il ne semble pas y avoir de hook pour vous permettre d'éviter cela.

En retard pour la partie, mais voici un résumé des ajustements requirejs pour MVC 5.2.3 si vous devez gérer l’exigence skipValidation des fournisseurs de valeur skipValidation .

 public class TrimSsortingngModelBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { // First check if request validation is required var shouldPerformRequestValidation = controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled; // determine if the value provider is IUnvalidatedValueProvider, if it is, pass in the // flag to perform request validation (eg [AllowHtml] is set on the property) var unvalidatedProvider = bindingContext.ValueProvider as IUnvalidatedValueProvider; var valueProviderResult = unvalidatedProvider?.GetValue(bindingContext.ModelName, !shouldPerformRequestValidation) ?? bindingContext.ValueProvider.GetValue(bindingContext.ModelName); return valueProviderResult?.AttemptedValue?.Trim(); } } 

Global.asax

  protected void Application_Start() { ... ModelBinders.Binders.Add(typeof(ssortingng), new TrimSsortingngModelBinder()); ... } 

Il y a eu beaucoup de messages suggérant une approche d’atsortingbut. Voici un paquet qui a déjà un atsortingbut sortingm et beaucoup d’autres: Dado.ComponentModel.Mutations ou NuGet

 public partial class ApplicationUser { [Trim, ToLower] public virtual ssortingng UserName { get; set; } } // Then to preform mutation var user = new ApplicationUser() { UserName = " M@X_speed.01! " } new MutationContext(user).Mutate(); 

Après l’appel à Mutate (), user.UserName sera muté à m@x_speed.01! .

Cet exemple coupe les espaces et la case en minuscule. Il n’introduit pas de validation, mais System.ComponentModel.Annotations peut être utilisé avec Dado.ComponentModel.Mutations .