其实第一篇的敏感词过滤方法在遇到webApi和Http请求时不起作用,
所以我在后面又试了好几种方法,最后选择使用HttpParameterBinding的方法来实现api请求时的敏感词过滤
首先,在ExecuteBindingAsync中,我们使用actionContext.ActionArguments来获取对象baseModel(actionContext.ActionArguments好像有点问题,传递多个对象参数的时候每次都只拿到第一个?待研究……),
然后就是根据需要对对象的属性做过滤操作,最后再把过滤完的属性重新加回baseModel,
这边是仅过滤string类型的数据,而且一些CustomAttributes被排除在外了,这边完全可以自己定义Attributes然后实现只针对部分键进行过滤
public class TestHttpParameterBinding<T> : HttpParameterBinding { private readonly HttpParameterBinding _paramaterBinding; private readonly HttpParameterDescriptor _httpParameterDescriptor; public TestHttpParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor) { _httpParameterDescriptor = descriptor; _paramaterBinding = new FromBodyAttribute().GetBinding(descriptor); } public override async Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider, HttpActionContext actionContext, CancellationToken cancellationToken) { await _paramaterBinding.ExecuteBindingAsync(metadataProvider, actionContext, cancellationToken); var baseModel = actionContext.ActionArguments[_httpParameterDescriptor.ParameterName]; if (baseModel != null) { foreach(var o in baseModel.GetType().GetProperties()) { var count = o.CustomAttributes.Where(x => x.AttributeType.Name == "NotMappedAttribute").Count(); if (o.PropertyType == typeof(string) && o.GetValue(baseModel) != null && count <= 0) { var result = string.Empty; var value = o.GetValue(baseModel).ToString(); TestService.s_filters = new AppDbContext().WordFilters.Select(x => x.Word).ToArray(); bool flag = TestService.filter(value, out result); if (flag) { o.SetValue(baseModel, result); } } } } } }
然后在HttpConfiguration中添加
config.ParameterBindingRules.Insert(0, descriptor => typeof(DAL.Models.Work).IsAssignableFrom(descriptor.ParameterType) ? new TestHttpParameterBinding<DAL.Models.Work>(descriptor) : null);
大功告成~
===========================================分割线=============================================
其实在尝试过程中还试了很多别的办法,比如
1.还是重写IModelBinder来实现过滤
代码如下
public class Test2ModelBinder : System.Web.Http.ModelBinding.IModelBinder { public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext) { var model = Activator.CreateInstance(bindingContext.ModelType); foreach (var prop in bindingContext.PropertyMetadata) { // Retrieving attribute var attr = bindingContext.ModelType.GetProperty(prop.Key) .GetCustomAttributes(false) .OfType<DataMemberAttribute>() .FirstOrDefault(); // Overwriting name if attribute present var qsParam = (attr != null) ? attr.Name : prop.Key; // Setting value of model property based on query string value if (bindingContext.ValueProvider.GetValue(qsParam) == null || bindingContext.ModelType.GetProperty(prop.Key).PropertyType != typeof(string)) { continue; } var value = bindingContext.ValueProvider.GetValue(qsParam).AttemptedValue; //word filter var result_value = string.Empty; TestService.s_filters = new AppDbContext().WordFilters.Select(x => x.Word).ToArray(); bool flag = TestService.filter(value as string,out result_value); if (flag) { value = result_value; } //var result = Convert.ChangeType(value, bindingContext.ModelType.GetProperty(prop.Key).PropertyType); var property = bindingContext.ModelType.GetProperty(prop.Key); property.SetValue(model, value); } bindingContext.Model = model; return true; } }
这里面让我感觉很厉害的是Activator.CreateInstance(bindingContext.ModelType)可以根据类型直接帮你新建一个对象,WOW~
然而实际使用中因为项目中的entity可能非常复杂,不仅仅包括基础的那几种类型,最后做类型转换ChangeType总是出现问题所以还是放弃了这种方式
当然,在某些场景这种方法可能更好一些
最后也只需要HttpConfiguration加上就可以用了
var provider = new SimpleModelBinderProvider(typeof(DAL.Models.Work), new Test2ModelBinder()); config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
然后我在找类型转换时,发现了用JsonDeserialization来转换的,在这边也mark一下,说不定以后会用到0.0
public class TestModelBinder<T> : System.Web.Http.ModelBinding.IModelBinder { static TestModelBinder() { } public bool BindModel(HttpActionContext actionContext, System.Web.Http.ModelBinding.ModelBindingContext bindingContext) { if (bindingContext.ModelType != typeof(T)) { return false; } try { var json = ExtractRequestJson(actionContext); bindingContext.Model = DeserializeObjectFromJson(json); return true; } catch (JsonException exception) { bindingContext.ModelState.AddModelError("JsonDeserializationException", exception); return false; } } private static T DeserializeObjectFromJson(string json) { var binder = new TypeNameSerializationBinder(""); var obj = JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = binder }); return obj; } private static string ExtractRequestJson(HttpActionContext actionContext) { var content = actionContext.Request.Content; string json = content.ReadAsStringAsync().Result; return json; } }
public class TypeNameSerializationBinder : SerializationBinder { public string TypeFormat { get; private set; } public TypeNameSerializationBinder(string typeFormat) { TypeFormat = typeFormat; } public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = null; typeName = serializedType.Name; } public override Type BindToType(string assemblyName, string typeName) { string resolvedTypeName = string.Format(TypeFormat, typeName); return Type.GetType(resolvedTypeName, true); } }
2.也尝试了用IValueProvider来实现
首先
public class TestValueProvider : System.Web.Http.ValueProviders.IValueProvider { private Dictionary<string, string> _values; public TestValueProvider(HttpActionContext actionContext) { if (actionContext == null) { throw new ArgumentNullException("actionContext"); } _values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); var x = actionContext.Request.Content; } public bool ContainsPrefix(string prefix) { return _values.Keys.Contains(prefix); } public ValueProviderResult GetValue(string key) { string value; if (_values.TryGetValue(key, out value)) { return new ValueProviderResult(value, value, CultureInfo.InvariantCulture); } return null; } }
public class TestValueProviderFactory : ValueProviderFactory { public override System.Web.Http.ValueProviders.IValueProvider GetValueProvider(HttpActionContext actionContext) { return new TestValueProvider(actionContext); } }
然后也是在HttpConfiguration里加上
config.Services.Add(typeof(ValueProviderFactory), new TestValueProviderFactory());
结果是测试完全没进方法,待研究了……理论上用IValueProvider也是可行的,但限于时间没有作更多尝试