一.前言
通过各种姿势搜索都没搜到这方面的,唯一找到一个比较符合的,但是只适合简单类型,而且代码还没贴全,心累。。
然后查看官网和源码之后,发现继承并实现 IModelBinder和IModelBinderProvider 即可。
我这里是WebApi,所以要区分绑定属性[FromBody]、[FromForm]等。不同的绑定方法要实现不同的IModelBinder。
二:正文
api主要设计到的就是[FromBody]和[FromQuery],我这里也只实现了这两种,其余的方式没测试过。
public class StringTrimModelBinderProvider : IModelBinderProvider { private readonly IList_formatters; public StringTrimModelBinderProvider(IList formatters) { _formatters = formatters; } public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); if (!context.Metadata.IsComplexType && context.Metadata.ModelType == typeof(string)) { //简单类型 var loggerFactory = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory)); return new SimpleStringTrimModelBinder(context.Metadata.ModelType); } else if (context.BindingInfo.BindingSource != null && context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Body)) { //通过[FromBody]绑定的 return new BodyStringTrimModelBinder(_formatters, context.Services.GetRequiredService ()); } //else //if (context.Metadata.IsComplexType && !context.Metadata.IsCollectionType) //{ // //复杂类型 // var propertyBinders = context.Metadata.Properties // .ToDictionary(modelProperty => modelProperty, modelProperty => context.CreateBinder(modelProperty)); // var loggerFactory = (ILoggerFactory)context.Services.GetService(typeof(ILoggerFactory)); // return new AComplexTypeModelBinder(propertyBinders); //} return null; } }
下面的是实现IModelBinder
public class SimpleStringTrimModelBinder : IModelBinder { private readonly Type _type; public SimpleStringTrimModelBinder(Type type) { _type = type; } public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } var valueProvider = bindingContext.ValueProvider; var modelName = bindingContext.ModelName; var valueProviderResult = valueProvider.GetValue(modelName); if (valueProviderResult == ValueProviderResult.None) { return Task.CompletedTask; } string value = valueProviderResult.FirstValue.Trim(); //bindingContext.ModelState.SetModelValue(modelName, new ValueProviderResult(value)); //替换原有ValueProvider bindingContext.ValueProvider = new CompositeValueProvider { new ElementalValueProvider(modelName, value, valueProviderResult.Culture), bindingContext.ValueProvider }; //调用默认系统绑定 SimpleTypeModelBinder simpleTypeModelBinder = new SimpleTypeModelBinder(_type, (ILoggerFactory)bindingContext.HttpContext.RequestServices.GetService(typeof(ILoggerFactory))); simpleTypeModelBinder.BindModelAsync(bindingContext); //bindingContext.Result = ModelBindingResult.Success(value); return Task.CompletedTask; } }
public class BodyStringTrimModelBinder : IModelBinder { private readonly BodyModelBinder bodyModelBinder; public BodyStringTrimModelBinder(IListformatters, IHttpRequestStreamReaderFactory readerFactory) { bodyModelBinder = new BodyModelBinder(formatters,readerFactory); } public Task BindModelAsync(ModelBindingContext bindingContext) { if (bindingContext == null) { throw new ArgumentNullException(nameof(bindingContext)); } //调用原始body绑定数据 bodyModelBinder.BindModelAsync(bindingContext); //判断是否设置了值 if (!bindingContext.Result.IsModelSet) { return Task.CompletedTask; } //获取绑定对象 var model = bindingContext.Result.Model; /*通过反射修改值, 也可以实现 IInputFormatter接口里面的ReadAsync方法,自己从Request.Body里面获取数据进行处理,但是那样考虑的比较多也比较复杂,原谅我能力有限。。*/ var stringPropertyInfo = model.GetType().GetProperties().Where(c=>c.PropertyType == typeof(string)); foreach (PropertyInfo property in stringPropertyInfo) { string value = property.GetValue(model)?.ToString()?.Trim(); property.SetValue(model, value); } //bindingContext.Result = ModelBindingResult.Success(value); return Task.CompletedTask; } }
最后,需要将我们自定义的在Startup注册进去,
services.AddMvc(options => { //需要插入到第一条,内置默认是匹配到合适的Provider就不会在向下继续绑定;如果添加到末尾,即不会调用到我们实现的 options.ModelBinderProviders.Insert(0,new StringTrimModelBinderProvider(options.InputFormatters)); })
记录成长中的点点滴滴。。