Veri girişinden sonra dizeleri kırpmanın en iyi yolu. Özel bir model bağlayıcı oluşturmalı mıyım?


172

ASP.NET MVC kullanıyorum ve tüm kullanıcı girilen dize alanları veritabanına eklenmeden önce kesilmiş istiyorum. Birçok veri giriş formum olduğu için, kullanıcı tarafından sağlanan her dize değerini açıkça kırpmak yerine tüm dizeleri kırpmak için zarif bir yol arıyorum. İnsanların ipleri nasıl ve ne zaman kırptığını bilmek istiyorum.

Belki de özel bir model bağlayıcı oluşturma ve orada herhangi bir dize değerleri kırpmayı düşündüm ... bu şekilde, tüm kırpma mantığı tek bir yerde bulunur. Bu iyi bir yaklaşım mı? Bunu yapan kod örnekleri var mı?

Yanıtlar:


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

      base.SetProperty(controllerContext, bindingContext, 
                          propertyDescriptor, value);
    }
  }

Peki ya bu kod?

ModelBinders.Binders.DefaultBinder = new TrimModelBinder();

Global.asax Application_Start olayını ayarlayın.


3
ben sadece iç en {} kısaca bu kodu yerine: string stringValue = (string) değeri; value = string.IsNullOrEmpty (stringValue)? stringValue: stringValue.Trim ();
Simon_Weaver

4
Bu daha fazla oyu hak ediyor. MVC ekibinin bunu varsayılan model bağlayıcıda uygulamayı seçmediğine şaşırdım ...
Portman

1
@BreckFresen Aynı sorunu yaşadım, BindModel yöntemini geçersiz kılmanız ve bir string için bağlamaContext.ModelType'ı kontrol etmeniz ve varsa kırpmanız gerekir.
Kelly

3
Benim gibi herkes DefaultModelBinder üzerinde bir belirsizlik elde etmek için doğru olanı System.Web.Mvc kullanıyor.
GeoffM

3
type="password"Girişlere dokunulmadan bırakmak için bunu nasıl değiştirirsiniz ?
Extragorey

77

Bu @takepara ile aynı çözünürlüktedir ancak DefaultModelBinder yerine IModelBinder olarak global.asax içinde model bağlayıcı eklemek

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

Sınıf:

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 == string.Empty)
           return string.Empty;
        return valueResult.AttemptedValue.Trim();
    }
}

@haacked gönderiye göre: http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx


1
Temiz bir çözüm için +1! returnİfadelerin sırasını değiştirerek ve koşulu reddederek kodunuzun okunabilirliğini daha da artırabilirsiniz :if (valueResult == null || string.IsNullOrEmpty(valueResult.AttemptedValue)) return null;
Marius Schulz

6
Bu, [ValidateInput (false)] denetleyici özniteliğini işlemez. "Tehlikeli Talep ..." istisnasına neden olur.
CodeGrue

2
'Tehlikeli Talep ...' istisnası olanlar için bu makaleye bakın - blogs.taiga.nl/martijn/2011/09/29/…
GurjeetSinghDB

2
Bir iş arkadaşım, bunun her türlü soruna neden olan bir varyasyonu uyguladı: issue.umbraco.org/issue/U4-6665 Her zaman birini diğerinden daha fazla tercih etmek yerine null ve boş olarak uygun şekilde geri döndürmenizi öneririm (sizin durumunuzda, değer boş bir dize olsa bile her zaman null değerini döndürür).
Nicholas Westby

2
Bu [AllowHtml], model özelliklerindeki niteliği kırıyor gibi görünüyor ( [ValidateInput(false)]yukarıda CodeGrue'un belirttiği gibi
Mingwei Samuel

43

@Takepara cevabında bir gelişme.

Projede biraz:

public class NoTrimAttribute : Attribute { }

TrimModelBinder sınıf değişikliğinde

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

için

if (propertyDescriptor.PropertyType == typeof(string) && !propertyDescriptor.Attributes.Cast<object>().Any(a => a.GetType() == typeof(NoTrimAttribute)))

kırpma dışında bırakılacak özellikleri [NoTrim] özniteliğiyle işaretleyebilirsiniz.


1
@Korayem'in IModelBinder yaklaşımını kullanırken bu özellik gibi bir şeyi nasıl uygulayabiliriz? Bazı uygulamalarda, farklı bir (üçüncü taraf) model bağlayıcı kullanıyorum (örneğin, S # arp Archeticture's). Bu projeler arasında paylaşılan özel bir DLL yazmak istiyorum, bu yüzden bir IModelBinder yaklaşım olması gerekiyor.
Carl Bussema

1
@CarlBussema İşte bir IModelBinder içinden Özniteliklere erişme hakkında bir soru. stackoverflow.com/questions/6205176
Mac Saldırısı

4
Bence harika bir ektir ama yerini alacak düşünüyorum .Cast<object>().Any(a => a.GetType() == typeof(NoTrimAttribute))ile .OfType<NoTrimAttribute>().Any(). Biraz daha temiz.
DBueno

Veri ek açıklamalarında olduğu gibi, bu niteliklerin yalnızca MVC'den (örneğin, iş katmanı, istemciler) daha geniş bir kullanım kapsamına sahip olduğu için özniteliklerimi paylaşılan bir derlemeye koydum. Bir başka gözlem olan "DisplayFormatAttribute (ConvertEmptyStringToNull)", kesilen dizenin boş veya boş bir dize olarak kaydedilip kaydedilmeyeceğini denetler. Varsayılan ı like true (null) ama veritabanınızda boş dizeleri gerektiren (umarım değil) bunu almak için yanlış ayarlayabilirsiniz. Her neyse, hepsi bu iyi şeyler, umarım MS, kırpma ve dolgu ve bunun gibi birçok yaygın şeyi içerecek şekilde özelliklerini genişletir.
Tony Wall

17

C # 6'daki iyileştirmelerle, artık tüm dize girişlerini kesecek çok kompakt bir model bağlayıcı yazabilirsiniz:

public class TrimStringModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        var attemptedValue = value?.AttemptedValue;

        return string.IsNullOrWhiteSpace(attemptedValue) ? attemptedValue : attemptedValue.Trim();
    }
}

Bu işlem Ödemeler bir yere eklemeniz gerekir Application_Start()senin içinde Global.asax.csbağlayıcı modeli bağlayıcı kullanmak dosyanın strings:

ModelBinders.Binders.Add(typeof(string), new TrimStringModelBinder());

Varsayılan model bağlayıcıyı geçersiz kılmak yerine böyle bir model bağlayıcı kullanmak daha iyidir, çünkü o zaman stringbir yöntem argümanı olarak mı yoksa bir model sınıfında bir özellik olarak mı bağlanırsanız kullanılacaktır. Ancak, burada diğer yanıtların önerdiği gibi varsayılan model bağlayıcıyı geçersiz kılarsanız, sadece modellerde özellikler bağlayıcı işi yaparken, değil bir olduğunda stringbir eylem yöntemine bağımsız değişken olarak

Düzenleme: Bir yorumcu, bir alanın doğrulanmaması gerektiğinde durumla ilgili sorular sordu. Orijinal cevabım sadece OP'nin sormuş olduğu soru ile başa çıkmak için azaltıldı, ancak ilgilenenler için aşağıdaki genişletilmiş model bağlayıcıyı kullanarak doğrulama ile başa çıkabilirsiniz:

public class TrimStringModelBinder : 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 string.IsNullOrWhiteSpace(attemptedValue) ? attemptedValue : attemptedValue.Trim();
    }
}

Yine, yukarıdaki yorumlara bakın. Bu örnek, IUnvalidatedValueProvider öğesinin skipValidation gereksinimini işlemez.
Aaron Hudon

@adrian, IModelBinder arabirimi yalnızca dönüş tipi boole sahip BindModel yöntemine sahiptir. O zaman burada dönüş türü nesnesiyle nasıl kullandınız?
Magendran V

@MagendranV Hangi arabirime baktığınızdan emin değilim, ancak bu cevap ASP.NET MVC 5 içindeki bir nesneyi döndüren IModelBinder'a dayanıyor
adrian

1
@AaronHudon Cevabımı atlama doğrulamasının üstesinden gelmek için bir örnek içerecek şekilde güncelledim
adrian

Parola alanlarınız doğru veri türü kümesine sahipse (örn. [DataType (DataType.Password)]), son satırı aşağıdaki alanları güncelleyecek şekilde güncelleyebilirsiniz: return string.IsNullOrWhiteSpace (attempttedValue) || bağlamaContext.ModelMetadata.DataTypeName == "Parola"? attempttedValue: attempttedValue.Trim ();
trfletch

15

In ASP.Net Core 2 Bu benim için çalıştı. Ben kullanıyorum [FromBody]benim denetleyicileri ve JSON girişteki özniteliği. JSON serileştirme dizesi işleme geçersiz kılmak için kendi JsonConverter kaydetti:

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

Ve bu dönüştürücü:

public class TrimmingStringConverter : JsonConverter
{
    public override bool CanRead => true;
    public override bool CanWrite => false;

    public override bool CanConvert(Type objectType) => objectType == typeof(string);

    public override object ReadJson(JsonReader reader, Type objectType,
        object existingValue, JsonSerializer serializer)
    {
        if (reader.Value is string value)
        {
            return value.Trim();
        }

        return reader.Value;
    }

    public override void WriteJson(JsonWriter writer, object value,
        JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Çözümünüz iyi çalışıyor! Teşekkürler. IModelBinderProvider kullanarak .Net Core için diğer çözümleri denedim, işe yaramadı.
Cedric Arnould

Startup.cs dışında, Modelde [JsonConverter (typeof (TrimmingStringConverter))] olarak da kullanılabilir. Btw. Bunun yerine .Insert () kullanmanın bir nedeni var mı?
idin

@wast Sanırım .Add () yerine .Insert () yerine diğer Dönüştürücüler'den önce çalışmasını sağlamak için yaptım. Şimdi hatırlayamıyorum.
Kai G

Bunun DefaultContractResolver üzerindeki performans yükü nedir?
Maulik Modi

13

@ Takepara'nın cevabının başka bir varyasyonu, ancak farklı bir bükülme ile:

1) Ben tercih "StringTrim" öznitelik mekanizması tercih (@Anton devre dışı "NoTrim" örneği yerine).

2) ModelState'in doğru şekilde doldurulduğundan emin olmak için SetModelValue öğesine ek bir çağrı yapılması ve varsayılan doğrulama / kabul / reddetme kalıbı normal olarak kullanılabilir, yani uygulamak için TryUpdateModel (model) ve tüm değişiklikleri kabul etmek için ModelState.Clear ().

Bunu varlık / paylaşılan kitaplığınıza koyun:

/// <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
{
}

Ardından bu MVC uygulamanızda / kütüphanenizde:

/// <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;
    }
}

Özellik değerini ciltte ayarlamazsanız, hiçbir şeyi değiştirmek istemeseniz bile, bu özelliği ModelState'ten tamamen engelleyeceksiniz! Bunun nedeni, tüm dize türlerini bağlama olarak kayıtlı olmanızdır, bu nedenle (bağlantımda) varsayılan ciltleyicinin sizin için yapamayacağı görünür.


7

ASP.NET Core 1.0'da nasıl yapılacağını arayan herkes için ek bilgi. Mantık oldukça değişti.

Nasıl yapılacağı hakkında bir blog yazısı yazdım , biraz daha ayrıntılı olarak açıklıyor

ASP.NET Core 1.0 çözümü:

Gerçek kırpmayı yapmak için model bağlayıcı

public class TrimmingModelBinder : ComplexTypeModelBinder  
{
    public TrimmingModelBinder(IDictionary propertyBinders) : base(propertyBinders)
    {
    }

    protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
    {
        if(result.Model is string)
        {
            string resultStr = (result.Model as string).Trim();
            result = ModelBindingResult.Success(resultStr);
        }

        base.SetProperty(bindingContext, modelName, propertyMetadata, result);
    }
}

Ayrıca en son sürümde Model Binder Provider'a ihtiyacınız var, bu binder'in bu model için kullanılması gerektiğini söyler

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;
    }
}

Ardından Startup.cs dosyasına kaydedilmelidir.

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

Benim için de işe yaramadı, tüm alanlar şimdi boş
Cedric Arnould

5

Yukarıdaki mükemmel cevapları ve yorumları okurken ve giderek karışırken, aniden düşündüm, hey, bir jQuery çözümü olup olmadığını merak ediyorum. Benim gibi ModelBinders'ı biraz şaşkınlık duyan diğerleri için, form gönderilmeden önce giriş alanlarını kesen aşağıdaki jQuery snippet'ini sunuyoruz.

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

1
2 şey: 1 - İstemci nesnelerinizi önbelleğe alın ($ (this) gibi), 2 - İstemci girişlerine asla güvenemezsiniz, ancak kesinlikle sunucu koduna güvenebilirsiniz. Cevabınız sunucu kodu cevaplarına bir tamamlamadır :)
graumanoz

5

MVC Çekirdeği durumunda

bağlayıcı:

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 string str &&
            !string.IsNullOrEmpty(str))
        {
            bindingContext.Result = ModelBindingResult.Success(str.Trim());
            return Task.CompletedTask;
        }

        return FallbackBinder.BindModelAsync(bindingContext);
    }
}

Sağlayıcı:

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(string))
        {
            return new TrimmingModelBinder(new SimpleTypeModelBinder(context.Metadata.ModelType));
        }

        return null;
    }
}

Kayıt fonksiyonu:

    public static void AddStringTrimmingProvider(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());
    }

Kayıt ol:

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

+1. Tam aradığım şey. Kayıt işlevindeki "binderToFind" kodunun amacı nedir?
Brad

Ben sadece SimpleTypeModelBinderProvideraynı dizini koruyarak düşüş ile özel sağlayıcı koymak için çalışıyorum .
Vikash Kumar

Bütün açıklama burada bulunabilir vikutech.blogspot.in/2018/02/…
Vikash Kumar

3

Partiye geç, ancak eğer skipValidationyerleşik değer sağlayıcılarının gereksinimlerini karşılayacaksanız, MVC 5.2.3 için gerekli ayarlamaların bir özeti aşağıdadır .

public class TrimStringModelBinder : 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 (e.g. [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(string), new TrimStringModelBinder());
        ...
    }

2

Çözüme katılmıyorum. SetProperty verileri ModelState tarafından da doldurulabileceğinden GetPropertyValue değerini geçersiz kılmalısınız. Ham verileri giriş öğelerinden yakalamak için şunu yazın:

 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);

        string retval = value as string;

        return string.IsNullOrWhiteSpace(retval)
                   ? value
                   : retval.Trim();
    }

}

PropertyDescriptor'a göre filtrele Eğer gerçekten sadece dize değerleriyle ilgileniyorsanız ama önemli olmamalı çünkü gelen her şey temelde bir dize.


2

İçin ASP.NET Çekirdek , yerine ComplexTypeModelBinderProviderdizeleri kırpar bir sağlayıcı ile.

Başlangıç ​​kodu ConfigureServicesyönteminize şunu ekleyin:

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

Şunun TrimmingModelBinderProvidergibi tanımlayın :

/// <summary>
/// Used in place of <see cref="ComplexTypeModelBinderProvider"/> to trim beginning and ending whitespace from user input.
/// </summary>
class TrimmingModelBinderProvider : IModelBinderProvider
{
    class TrimmingModelBinder : ComplexTypeModelBinder
    {
        public TrimmingModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders) : base(propertyBinders) { }

        protected override void SetProperty(ModelBindingContext bindingContext, string modelName, ModelMetadata propertyMetadata, ModelBindingResult result)
        {
            var value = result.Model as string;
            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<ModelMetadata, IModelBinder>();
            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;
    }
}

Bunun çirkin kısmı, GetBindermantığın kopyalanması ve yapıştırılmasıdır ComplexTypeModelBinderProvider, ancak bundan kaçınmanıza izin veren herhangi bir kanca görünmemektedir.


Neden bilmiyorum, ama ASP.NET Core 1.1.1 için çalışmıyor. Denetleyici eyleminde aldığım model nesnesinin tüm özellikleri null. "SetProperty" yöntemi nerver olarak adlandırılır.
Waldo

Benim için çalışmadı, mülkümün başındaki alan hala orada.
Cedric Arnould

2

Sorgu dizesi parametre değerlerini ve form değerlerini kırpmak için değer sağlayıcıları oluşturdum. Bu ASP.NET Core 3 ile test edildi ve mükemmel çalışıyor.

public class TrimmedFormValueProvider
    : FormValueProvider
{
    public TrimmedFormValueProvider(IFormCollection values)
        : base(BindingSource.Form, values, CultureInfo.InvariantCulture)
    { }

    public override ValueProviderResult GetValue(string key)
    {
        ValueProviderResult baseResult = base.GetValue(key);
        string[] trimmedValues = baseResult.Values.Select(v => v?.Trim()).ToArray();
        return new ValueProviderResult(new StringValues(trimmedValues));
    }
}

public class TrimmedQueryStringValueProvider
    : QueryStringValueProvider
{
    public TrimmedQueryStringValueProvider(IQueryCollection values)
        : base(BindingSource.Query, values, CultureInfo.InvariantCulture)
    { }

    public override ValueProviderResult GetValue(string key)
    {
        ValueProviderResult baseResult = base.GetValue(key);
        string[] trimmedValues = baseResult.Values.Select(v => v?.Trim()).ToArray();
        return new ValueProviderResult(new StringValues(trimmedValues));
    }
}

public class TrimmedFormValueProviderFactory
    : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        if (context.ActionContext.HttpContext.Request.HasFormContentType)
            context.ValueProviders.Add(new TrimmedFormValueProvider(context.ActionContext.HttpContext.Request.Form));
        return Task.CompletedTask;
    }
}

public class TrimmedQueryStringValueProviderFactory
    : IValueProviderFactory
{
    public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
    {
        context.ValueProviders.Add(new TrimmedQueryStringValueProvider(context.ActionContext.HttpContext.Request.Query));
        return Task.CompletedTask;
    }
}

Ardından, değer sağlayıcı fabrikalarını ConfigureServices()Startup.cs içindeki işleve kaydedin

services.AddControllersWithViews(options =>
{
    int formValueProviderFactoryIndex = options.ValueProviderFactories.IndexOf(options.ValueProviderFactories.OfType<FormValueProviderFactory>().Single());
    options.ValueProviderFactories[formValueProviderFactoryIndex] = new TrimmedFormValueProviderFactory();

    int queryStringValueProviderFactoryIndex = options.ValueProviderFactories.IndexOf(options.ValueProviderFactories.OfType<QueryStringValueProviderFactory>().Single());
    options.ValueProviderFactories[queryStringValueProviderFactoryIndex] = new TrimmedQueryStringValueProviderFactory();
});

0

Bir özellik yaklaşımı öneren çok sayıda yayın var. Burada zaten bir süs niteliği ve diğerleri olan bir pakettir: Dado.ComponentModel.Mutations veya Nuget

public partial class ApplicationUser
{
    [Trim, ToLower]
    public virtual string UserName { get; set; }
}

// Then to preform mutation
var user = new ApplicationUser() {
    UserName = "   M@X_speed.01! "
}

new MutationContext<ApplicationUser>(user).Mutate();

Mutate () çağrısından sonra user.UserName değiştirilir m@x_speed.01!.

Bu örnek, boşlukları kırpacak ve dizeyi küçük harfe çevirecektir. Doğrulama getirmez, ancak System.ComponentModel.Annotationsbirlikte kullanılabilir Dado.ComponentModel.Mutations.


0

Bu başka bir iş parçacığında gönderdi. Asp.net core 2'de farklı bir yöne gittim. Bunun yerine bir eylem filtresi kullandım. Bu durumda, geliştirici ya global olarak ayarlayabilir ya da dize kırpmayı uygulamak istediği eylemler için bir özellik olarak kullanabilir. Bu kod, model bağlama gerçekleştikten sonra çalışır ve model nesnesindeki değerleri güncelleyebilir.

İşte benim kod, önce bir eylem filtresi oluşturmak:

public class TrimInputStringsAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        foreach (var arg in context.ActionArguments)
        {
            if (arg.Value is string)
            {
                string val = arg.Value as string;
                if (!string.IsNullOrEmpty(val))
                {
                    context.ActionArguments[arg.Key] = val.Trim();
                }

                continue;
            }

            Type argType = arg.Value.GetType();
            if (!argType.IsClass)
            {
                continue;
            }

            TrimAllStringsInObject(arg.Value, argType);
        }
    }

    private void TrimAllStringsInObject(object arg, Type argType)
    {
        var stringProperties = argType.GetProperties()
                                      .Where(p => p.PropertyType == typeof(string));

        foreach (var stringProperty in stringProperties)
        {
            string currentValue = stringProperty.GetValue(arg, null) as string;
            if (!string.IsNullOrEmpty(currentValue))
            {
                stringProperty.SetValue(arg, currentValue.Trim(), null);
            }
        }
    }
}

Bunu kullanmak için genel filtre olarak kaydolun veya eylemlerinizi TrimInputStrings özniteliğiyle süsleyin.

[TrimInputStrings]
public IActionResult Register(RegisterViewModel registerModel)
{
    // Some business logic...
    return Ok();
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.