.NET 4.7 API通用字符串HTML编码方法实现求助
解决.NET 4.7 API中全局HtmlEncode所有提交对象字符串的问题
需要实现一个通用方法,能接收任意对象并遍历其所有值,对每个字符串执行HtmlEncode操作,且该方法要作用于提交到.NET 4.7 API控制器的所有对象。已创建ActionFilterAttribute并添加到全局过滤器,处理“扁平”对象正常,但无法处理包含IList成员或带字符串属性的嵌套对象的复杂结构,多次尝试未解决。
现有初始代码
public override void OnActionExecuting(HttpActionContext actionContext) { // 对PUT或POST请求中的字符串进行编码 if (actionContext.Request.Method.ToString() == WebRequestMethods.Http.Post || actionContext.Request.Method.ToString() == WebRequestMethods.Http.Put) { // 遍历PUT/POST中的每个参数 foreach (var item in actionContext.ActionArguments.Values) { try { var type = item.GetType(); // 遍历对象的每个属性,如果是字符串则执行HtmlEncode foreach (PropertyInfo propertyInfo in type.GetProperties()) { var prop = propertyInfo.GetValue(item); if (prop is string str) { propertyInfo.SetValue(item, WebUtility.HtmlEncode(str)); } } } catch (Exception) { // 忽略异常 } } } base.OnActionExecuting(actionContext); }
存在问题的测试代码
该代码会将Int类型识别为Object,导致错误递归:
public class HttpStringDecodeFilter : ActionFilterAttribute { private void HtmlEncodeAllStringsInObject(Object obj) { var type = obj.GetType(); var properties = type.GetProperties(); foreach (var property in properties) { var propValue = property.GetValue(obj); if (property.PropertyType.IsAssignableFrom(typeof(string))) { property.SetValue(obj, WebUtility.HtmlEncode(propValue?.ToString())); } else if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)) { if (propValue is not List<string> list) continue; for (var i = 0; i < list.Count; i++) { list[i] = WebUtility.HtmlEncode(list[i]); } property.SetValue(obj, list); } else { var typeCode = Type.GetTypeCode(property.PropertyType); if (typeCode.Equals(TypeCode.Object)) { HtmlEncodeAllStringsInObject(property); } } } } }
修正后的完整实现
下面的代码解决了嵌套对象、泛型列表(包括非字符串元素的列表)、值类型误判的问题:
public class HtmlEncodeActionFilter : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (actionContext.Request.Method == HttpMethod.Post || actionContext.Request.Method == HttpMethod.Put) { foreach (var argValue in actionContext.ActionArguments.Values) { if (argValue == null) continue; ProcessObject(argValue); } } base.OnActionExecuting(actionContext); } private void ProcessObject(object obj) { if (obj == null) return; var type = obj.GetType(); // 处理泛型集合(IList<T>) if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>)) { var list = (IEnumerable)obj; foreach (var item in list) { // 如果是字符串,直接编码并替换列表中的元素 if (item is string str) { var index = ((IList)obj).IndexOf(item); ((IList)obj)[index] = WebUtility.HtmlEncode(str); } else { // 非字符串元素,递归处理对象 ProcessObject(item); } } return; } // 处理值类型(除了字符串)和枚举,直接跳过 if (type.IsValueType || type.IsEnum) { return; } // 处理嵌套对象的属性 foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!property.CanRead || !property.CanWrite) continue; var propValue = property.GetValue(obj); // 处理字符串属性 if (property.PropertyType == typeof(string)) { if (propValue is string strValue) { property.SetValue(obj, WebUtility.HtmlEncode(strValue)); } continue; } // 处理可空类型(比如int?),获取其底层值类型 var underlyingType = Nullable.GetUnderlyingType(property.PropertyType); if (underlyingType != null && underlyingType.IsValueType) { continue; } // 递归处理嵌套对象或集合 ProcessObject(propValue); } } }
关键修复点
- 集合处理优化:不再局限于
List<string>,兼容所有IList<T>类型,遍历集合元素时,字符串直接编码替换,非字符串对象递归处理。 - 值类型过滤:明确跳过值类型(字符串除外)和枚举,避免将int等值类型误判为Object触发无效递归。
- 可空类型识别:处理
Nullable<T>类型,跳过值类型的可空变体,防止不必要的递归操作。 - 空值防护:全程增加空值判断,避免空引用异常。
- 属性权限校验:仅处理可读可写的公共实例属性,避免操作只读或私有属性引发错误。
内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

