数据输入后修剪字符串的最佳方法。我应该创建自定义模型活页夹吗?


172

我正在使用ASP.NET MVC,我希望在将所有用户输入的字符串字段插入数据库之前先对其进行修剪。由于我有许多数据输入表单,因此我正在寻找一种修整所有字符串的方法,而不是显式修整每个用户提供的字符串值。我很想知道人们何时以及如何修剪琴弦。

我考虑过可能要创建一个自定义模型联编程序并在那里修剪任何字符串值...那样,我所有的修剪逻辑都包含在一个地方。这是一个好方法吗?有没有执行此操作的代码示例?

Answers:


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

这个代码怎么样?

ModelBinders.Binders.DefaultBinder = new TrimModelBinder();

设置global.asax Application_Start事件。


3
为了简洁起见,我只用最下面的{}替换代码:string stringValue =(string)value; 值= string.IsNullOrEmpty(stringValue)?stringValue:stringValue.Trim();
Simon_Weaver

4
这值得更多的投票。实际上,我很惊讶MVC团队没有选择在默认的模型绑定器中实现这一点……
Portman 2010年

1
@BreckFresen我有同样的问题,您将需要重写BindModel方法并检查bindingContext.ModelType是否为字符串,然后修剪是否为字符串。
凯利

3
对于像我这样在DefaultModelBinder上含糊不清的任何人,正确的方法是使用System.Web.Mvc。
GeoffM '16

3
您将如何修改它以type="password"保持输入不变?
Extragorey

77

这是@takepara相同的分辨率,但与IModelBinder而不是DefaultModelBinder相同,因此可以通过以下方式在global.asax中添加modelbinder

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

班上:

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帖子:http ://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx


1
+1为一个干净的解决方案!您可以通过更改return语句的顺序并取消条件来进一步提高代码的可读性:if (valueResult == null || string.IsNullOrEmpty(valueResult.AttemptedValue)) return null;
Marius Schulz

6
这不处理[ValidateInput(false)]控制器属性。它将导致“危险请求...”异常。
CodeGrue 2012年

2
对于那些收到“危险请求...”例外的人,请参考此文章-blogs.taiga.nl/martijn/2011/09/29/…– GurjeetSinghDB 2014
48

2
我的一位同事实施了这种更改,导致了各种各样的问题:issue.umbraco.org/issue/U4-6665 我建议适当地返回null和empty,而不是总是偏向于另一个(在您的情况下,您即使该值为空字符串,也始终返回null)。
Nicholas Westby

2
这似乎破坏了[AllowHtml]模型属性的属性(以及[ValidateInput(false)]上面提到的CodeGrue
Samuel

43

@takepara答案的一项改进。

在项目中:

public class NoTrimAttribute : Attribute { }

在TrimModelBinder类中更改

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

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

您可以使用[NoTrim]属性标记要修剪的属性。


1
当使用@Korayem的IModelBinder方法时,如何实现类似此属性的东西?在某些应用程序中,我使用不同的(第三方)模型活页夹(例如,S#arp Archeticture的活页夹)。我想将其写在项目之间共享的私有DLL中,因此它必须是IModelBinder方法。
卡尔·布塞玛

1
@CarlBussema这是一个有关从IModelBinder内部访问属性的问题。stackoverflow.com/questions/6205176
Mac攻击

4
我认为这是一个很好的补充,但我会更换.Cast<object>().Any(a => a.GetType() == typeof(NoTrimAttribute)).OfType<NoTrimAttribute>().Any()。只是一点点清洁。
DBueno

我将属性放在共享程序集中,因为与数据注释一样,此类属性的使用范围比MVC(例如业务层,客户端)更广泛。另一个观察结果是“ DisplayFormatAttribute(ConvertEmptyStringToNull)”控制修剪后的字符串将被保存为null还是空字符串。默认值是true(空),我喜欢,但是如果您在数据库中需要空字符串(希望不是),则可以将其设置为false以获得该值。无论如何,这都是好东西,希望MS扩展其属性以包括修整和填充以及许多其他常见的东西。
Tony Wall,

17

通过C#6的改进,您现在可以编写一个非常紧凑的模型活页夹,该活页夹将修剪所有字符串输入:

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

您需要Application_Start()Global.asax.cs文件中的某处包含此行,以便在绑定strings 时使用模型绑定器:

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

我发现最好使用这样的模型联编程序,而不是覆盖默认的模型联编程序,因为这样,无论您将a string直接绑定为方法参数还是模型类的属性,都将使用它。但是,如果您按照此处建议的其他答案覆盖默认的模型绑定器,则当绑定模型上的属性时才有效,当您string将action方法的参数作为参数时才起作用

编辑:评论者问有关不应该验证字段时的处理情况。我最初的回答被简化为仅处理OP提出的问题,但是对于那些感兴趣的人,您可以使用以下扩展的模型绑定器来进行验证:

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

同样,请参见上面的评论。本示例不处理IUnvalidatedValueProvider的skipValidation要求。
亚伦·休顿

@ adrian,IModelBinder接口只有返回类型为bool的BindModel方法。那么您如何在这里使用return类型对象?
Magendran V

@MagendranV我不确定您要查看哪个接口,但是此答案基于ASP.NET MVC 5中的IModelBinder,它返回一个对象:docs.microsoft.com/zh-cn/previous-versions/aspnet /…
adrian

1
@AaronHudon我更新了答案,以包含处理跳过验证的示例
adrian

如果您的密码字段设置了正确的数据类型(即[DataType(DataType.Password)]),则可以如下更新最后一行,以使它不修剪这些字段:返回string.IsNullOrWhiteSpace(attemptedValue)|| bindingContext.ModelMetadata.DataTypeName ==“密码”?trypedValue:trypedValue.Trim();
trfletch

15

ASP.Net Core 2中,这对我有用。我[FromBody]在控制器和JSON输入中使用属性。为了覆盖JSON反序列化中的字符串处理,我注册了自己的JsonConverter:

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

这是转换器:

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

您的解决方案工作正常!谢谢。我使用IModelBinderProvider尝试了.Net Core的其他解决方案,但是没有用。
Cedric Arnould

除了startup.cs以外,它还可以在模型中用作[JsonConverter(typeof(TrimmingStringConverter))]。顺便说一句。使用.Insert()代替.Add()背后有什么原因吗?
浪费了

@wast我想我只是做了.Insert()而不是.Add()以确保它在其他Converter之前运行。现在不记得了。
Kai G

通过DefaultContractResolver进行此操作的性能开销是多少?
莫利克·莫迪

13

@takepara答案的另一个变体,但又有不同的变化:

1)我更喜欢选择加入“ StringTrim”属性机制(而不是@Anton的选择退出“ NoTrim”示例)。

2)需要额外调用SetModelValue以确保正确填充ModelState,并且可以正常使用默认的验证/接受/拒绝模式,即,应用TryUpdateModel(model)和接受所有更改的ModelState.Clear()。

将其放在您的实体/共享库中:

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

然后在您的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;
    }
}

如果您没有在活页夹中设置属性值,即使您不想更改任何内容,也将完全阻止该属性进入ModelState!这是因为您已注册为绑定所有字符串类型,因此(在我的测试中)默认绑定器似乎不会为您执行此操作。


7

任何在ASP.NET Core 1.0中搜索如何执行此操作的人的额外信息。逻辑发生了很大变化。

我写了一篇有关如何做的博客文章,它对事情进行了更详细的解释

因此,ASP.NET Core 1.0解决方案:

模型活页夹进行实际修剪

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

此外,您还需要最新版本的Model Binder Provider,这表明该模型应使用此Binder

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

然后必须在Startup.cs中注册

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

它也对我不起作用,我所有的字段现在都为空
Cedric Arnould

5

在阅读了上面的出色答案和评论并变得越来越困惑的同时,我突然想到,嘿,我想知道是否有jQuery解决方案。因此,对于像我一样感到有些困惑的其他人,我提供了以下jQuery代码段,这些代码段在提交表单之前修剪了输入字段。

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

1
2件事情:1-缓存您的客户端对象(例如$(this)),2-您永远不能依赖客户端输入,但是您绝对可以依赖服务器代码。因此,您的答案是服务器代码答案的完成:)
graumanoz

5

如果是MVC Core

活页夹:

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

提供者:

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

注册功能:

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

寄存器:

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

+1。正是我想要的。注册功能中“ binderToFind”代码的作用是什么?
布拉德(Brad)

我只是想SimpleTypeModelBinderProvider通过保留相同的索引来使自定义提供程序具有后备功能。
维卡什·库马尔

整个说明可以在这里找到vikutech.blogspot.in/2018/02/...
Vikash库马尔

3

晚了,但是如果要处理skipValidation内置值提供程序的要求,以下是MVC 5.2.3所需的调整摘要。

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

我不同意该解决方案。您应该重写GetPropertyValue,因为SetProperty的数据也可以由ModelState填充。要从输入元素中捕获原始数据,请编写以下代码:

 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 PropertyType进行过滤,但不要紧,因为其中的所有内容基本上都是字符串。


2

对于ASP.NET Core,将其替换为ComplexTypeModelBinderProvider修剪字符串的提供程序。

在您的启动代码ConfigureServices方法中,添加以下内容:

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

定义TrimmingModelBinderProvider如下:

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

丑陋的部分是GetBinder逻辑的复制和粘贴ComplexTypeModelBinderProvider,但是似乎没有任何挂钩可以避免这种情况。


我不知道为什么,但是它不适用于ASP.NET Core 1.1.1。我在控制器操作中获得的模型对象的所有属性均为空。“ SetProperty”方法被称为神经。
Waldo

没有为我工作,我财产开始处的空间仍然存在。
Cedric Arnould

2

我创建了值提供程序来修剪查询字符串参数值和表单值。这已经通过ASP.NET Core 3进行了测试,并且可以完美运行。

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

然后ConfigureServices()在Startup.cs 中的函数中注册值提供者工厂

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

有很多帖子建议使用属性方法。这是一个已经具有trim属性和许多其他属性的包:Dado.ComponentModel.MutationsNuGet

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()之后,user.UserName将被更改为m@x_speed.01!

本示例将修剪空格并将字符串大小写为小写。它没有引入验证,但是System.ComponentModel.Annotations可以与一起使用Dado.ComponentModel.Mutations


0

我将其发布在另一个线程中。在asp.net核心2中,我朝另一个方向发展。我改用了动作过滤器。在这种情况下,开发人员可以全局设置它,也可以将其用作他/她想要应用字符串修整的动作的属性。该代码在模型绑定完成后运行,并且可以更新模型对象中的值。

这是我的代码,首先创建一个动作过滤器:

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

要使用它,请注册为全局过滤器,或者使用TrimInputStrings属性装饰您的操作。

[TrimInputStrings]
public IActionResult Register(RegisterViewModel registerModel)
{
    // Some business logic...
    return Ok();
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.