敏感词过滤(二)

其实第一篇的敏感词过滤方法在遇到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;
        }
    }
View Code
    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);
        }
    }
View Code

 

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也是可行的,但限于时间没有作更多尝试

 

posted @ 2018-04-11 14:21  AdolphChen  阅读(330)  评论(0编辑  收藏  举报