我使用ASP.NET MVC,我想他们是插入到数据库之前要修剪的所有用户输入的字符串字段。 而且因为我有很多数据录入形式,我正在寻找一个优雅的方式来修整所有的字符串,而不是明确地微调每一个用户提供的字符串值。 我想知道如何以及何时人们修剪字符串。
我想过也许创建自定义的模型绑定和修剪任何字符串值存在...这样,我所有的修整逻辑包含在一个地方。 这是一个好的方法吗? 是否有任何代码样本做到这一点?
我使用ASP.NET MVC,我想他们是插入到数据库之前要修剪的所有用户输入的字符串字段。 而且因为我有很多数据录入形式,我正在寻找一个优雅的方式来修整所有的字符串,而不是明确地微调每一个用户提供的字符串值。 我想知道如何以及何时人们修剪字符串。
我想过也许创建自定义的模型绑定和修剪任何字符串值存在...这样,我所有的修整逻辑包含在一个地方。 这是一个好的方法吗? 是否有任何代码样本做到这一点?
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事件。
这是@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
一个改进@takepara答案。
Somewere项目:
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]属性微调排除。
在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
文件中使用绑定时模型绑定string
S:
ModelBinders.Binders.Add(typeof(string), new TrimStringModelBinder());
我觉得这是更好地使用模型绑定这个样子,而不是覆盖默认的模型绑定,因为无论什么时候你要绑定一个将要使用string
,不管是直接作为方法的参数或模型类的属性。 但是,如果您覆盖默认的模型绑定其他的答案在这里建议,将只对模型绑定属性时工作,而不是当你有一个string
作为参数的操作方法
编辑:一个评论者询问有关情况打交道时,域无法验证。 我原来的答案被减少到只处理与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();
}
}
在ASP.Net酷睿2本为我工作。 我使用的是[FromBody]
在我的控制器和JSON输入属性。 要覆盖在字符串中我注册了自己的JsonConverter反序列化JSON处理:
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();
}
}
@ takepara的答案,但有不同的扭曲另一种变体:
1)我更喜欢选择加入“StringTrim”属性机制(而不是选择退出“NoTrim”示例@Anton的)。
2)需要SetModelValue的额外呼叫,以确保ModelState中被正确填充和默认有效/接受/拒绝图案可以用作正常的,即TryUpdateModel(模型)来应用和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中,物业干脆! 这是因为您已注册为结合所有字符串类型,所以才出现(在我的测试),默认的粘结剂不会为你做那么。
任何人寻找如何在ASP.NET 1.0的核心做到这一点额外的信息。 逻辑已经改变了很多。
我写了一篇博客文章如何做到这一点 ,就说明了一点更详细的东西
因此,ASP.NET 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);
}
}
还需要在最新版本的模型绑定提供者,这告诉应该用粘合剂用于此模型
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());
});
在阅读通过优良的答案和上述评论,并越来越困惑,我突然想到,哎,我不知道是否有一个jQuery的解决方案。 因此,对于其他人谁像我一样,找到ModelBinders有点扑朔迷离,我提供以下jQuery的片断的形式被提交之前修剪输入字段。
$('form').submit(function () {
$(this).find('input:text').each(function () {
$(this).val($.trim($(this).val()));
})
});
在MVC核心的情况下,
活页夹:
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())
对于ASP.NET核心 ,取代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
,但似乎没有要任何挂钩,让你避免这种情况。
我不同意的解决方案。 你应该重写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的属性类型过滤器,如果你真的只在字符串值感兴趣,但它不应该的问题,因为一切东西进来基本上是一个字符串。
迟到了,但下面是MVC 5.2.3所需的调整的总结,如果你要处理skipValidation
集结价值供应商的要求。
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());
...
}
已经有很多帖子提示属性的办法。 下面是已经有一个装饰属性和许多其他包: Dado.ComponentModel.Mutations或的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();
通话变异后(),user.UserName会突变为m@x_speed.01!
。
这个例子将修剪的空白和案例为小写的字符串。 它不引入验证,但System.ComponentModel.Annotations
可以一起使用Dado.ComponentModel.Mutations
。