Joh*_*ika 169 asp.net asp.net-mvc model-binders
我正在使用ASP.NET MVC,我希望所有用户输入的字符串字段在插入数据库之前进行修剪.由于我有很多数据输入表单,我正在寻找一种优雅的方法来修剪所有字符串,而不是明确地修剪每个用户提供的字符串值.我很想知道人们如何以及何时修剪弦乐.
我想也许创建一个自定义模型绑定器并修剪那里的任何字符串值......这样,我所有的修剪逻辑都包含在一个地方.这是一个好方法吗?是否有任何代码示例执行此操作?
小智 210
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码怎么样?
ModelBinders.Binders.DefaultBinder = new TrimModelBinder();
Run Code Online (Sandbox Code Playgroud)
设置global.asax Application_Start事件.
Kor*_*yem 76
这是@takepara相同的分辨率,但作为IModelBinder而不是DefaultModelBinder,以便在global.asax中添加modelbinder是通过
ModelBinders.Binders.Add(typeof(string),new TrimModelBinder());
Run Code Online (Sandbox Code Playgroud)
班级:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
基于@haacked帖子:http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx
小智 41
@takepara答案的一个改进.
有些人在项目中:
public class NoTrimAttribute : Attribute { }
Run Code Online (Sandbox Code Playgroud)
在TrimModelBinder类中更改
if (propertyDescriptor.PropertyType == typeof(string))
Run Code Online (Sandbox Code Playgroud)
至
if (propertyDescriptor.PropertyType == typeof(string) && !propertyDescriptor.Attributes.Cast<object>().Any(a => a.GetType() == typeof(NoTrimAttribute)))
Run Code Online (Sandbox Code Playgroud)
并且您可以使用[NoTrim]属性标记要从修剪中排除的属性.
adr*_*ian 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();
}
}
Run Code Online (Sandbox Code Playgroud)
您需要Application_Start()在Global.asax.cs文件中的某处包含此行以在绑定时使用模型绑定器string:
ModelBinders.Binders.Add(typeof(string), new TrimStringModelBinder());
Run Code Online (Sandbox Code Playgroud)
我发现最好使用这样的模型绑定器,而不是覆盖默认的模型绑定器,因为无论何时绑定a 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();
}
}
Run Code Online (Sandbox Code Playgroud)
Kai*_*i G 14
在ASP.Net Core 2中,这对我有用.我[FromBody]在我的控制器和JSON输入中使用该属性.要覆盖JSON反序列化中的字符串处理,我注册了自己的JsonConverter:
services.AddMvcCore()
.AddJsonOptions(options =>
{
options.SerializerSettings.Converters.Insert(0, new TrimmingStringConverter());
})
Run Code Online (Sandbox Code Playgroud)
这是转换器:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
Ton*_*all 13
@ takepara答案的另一个变种,但有一个不同的转折:
1)我更喜欢选择加入"StringTrim"属性机制(而不是@Anton的选择退出"NoTrim"示例).
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
{
}
Run Code Online (Sandbox Code Playgroud)
然后在你的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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您没有在活页夹中设置属性值,即使您不想更改任何内容,也会完全阻止ModelState中的该属性!这是因为您注册为绑定所有字符串类型,因此它(在我的测试中)显示默认绑定器不会为您执行此操作.
在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);
}
}
Run Code Online (Sandbox Code Playgroud)
此外,您需要最新版本的Model Binder Provider,这表明此绑定器应该用于此模型
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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后它必须在Startup.cs中注册
services.AddMvc().AddMvcOptions(options => {
options.ModelBinderProviders.Insert(0, new TrimmingModelBinderProvider());
});
Run Code Online (Sandbox Code Playgroud)
我创建了值提供程序来修剪查询字符串参数值和表单值。这已经用 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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后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();
});
Run Code Online (Sandbox Code Playgroud)
在阅读了上面的出色答案和评论并变得越来越困惑时,我突然想到,嘿,我想知道是否有jQuery解决方案。因此,对于像我一样感到有些困惑的其他人,我提供了以下jQuery代码段,这些代码段在提交表单之前修剪了输入字段。
$('form').submit(function () {
$(this).find('input:text').each(function () {
$(this).val($.trim($(this).val()));
})
});
Run Code Online (Sandbox Code Playgroud)
小智 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);
}
}
Run Code Online (Sandbox Code Playgroud)
提供者:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
注册功能:
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());
}
Run Code Online (Sandbox Code Playgroud)
寄存器:
service.AddMvc(option => option.AddStringTrimmingProvider())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
43412 次 |
| 最近记录: |