一.前言
通過各種姿勢搜索都沒搜到這方面的,唯一找到一個比較符合的,但是只適合簡單類型,而且代碼還沒貼全,心累。。
然后查看官網和源碼之后,發現繼承並實現 IModelBinder和IModelBinderProvider 即可。
我這里是WebApi,所以要區分綁定屬性[FromBody]、[FromForm]等。不同的綁定方法要實現不同的IModelBinder。
二:正文
api主要設計到的就是[FromBody]和[FromQuery],我這里也只實現了這兩種,其余的方式沒測試過。
public class StringTrimModelBinderProvider : IModelBinderProvider { private readonly IList<IInputFormatter> _formatters; public StringTrimModelBinderProvider(IList<IInputFormatter> 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<IHttpRequestStreamReaderFactory>()); } //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(IList<IInputFormatter> formatters, 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)); })
記錄成長中的點點滴滴。。
