.NET Core中间件:如何替换含特定特性的属性值
在中间件中批量处理带IgnoreAttribute的属性/类
要实现这个需求,核心思路是在中间件中拦截请求/响应流,反序列化为通用对象后通过反射遍历处理带特性的成员,最后重新序列化写回流,具体步骤如下:
1. 中间件中拦截并复用请求/响应流
HttpContext的Request.Body和Response.Body默认是只能读取一次的流,所以需要先开启缓冲并复制到内存流,保证后续可以反复操作:
public class IgnoreAttributeMiddleware { private readonly RequestDelegate _next; public IgnoreAttributeMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { // 处理请求体 context.Request.EnableBuffering(); var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync(); context.Request.Body.Position = 0; // 重置流位置,留给后续中间件读取 // 处理请求内容,替换带Ignore特性的字段 var processedRequest = IgnoreAttributeProcessor.ProcessIgnoredProperties(requestBody); var modifiedRequestStream = new MemoryStream(Encoding.UTF8.GetBytes(processedRequest)); context.Request.Body = modifiedRequestStream; // 处理响应体:替换原始响应流为可读写的内存流 var originalResponseStream = context.Response.Body; using var responseBuffer = new MemoryStream(); context.Response.Body = responseBuffer; // 调用后续中间件 await _next(context); // 读取并处理响应内容 responseBuffer.Position = 0; var responseBody = await new StreamReader(responseBuffer).ReadToEndAsync(); var processedResponse = IgnoreAttributeProcessor.ProcessIgnoredProperties(responseBody); // 将处理后的响应写回原始流 responseBuffer.Position = 0; await responseBuffer.CopyToAsync(originalResponseStream); context.Response.Body = originalResponseStream; } }
2. 核心工具类:反射处理带特性的成员
写一个通用工具类,负责反序列化JSON、遍历对象成员检查IgnoreAttribute、替换为默认值,最后重新序列化:
using System.Reflection; using System.Text.Json; public static class IgnoreAttributeProcessor { public static string ProcessIgnoredProperties(string json) { var obj = JsonSerializer.Deserialize<object>(json); if (obj == null) return json; var objType = obj.GetType(); // 如果类本身标记了IgnoreAttribute,直接返回默认值的JSON if (objType.GetCustomAttribute<IgnoreAttribute>() != null) { var defaultValue = GetDefaultValue(objType); return JsonSerializer.Serialize(defaultValue); } RecursivelyProcessObject(obj); return JsonSerializer.Serialize(obj); } private static void RecursivelyProcessObject(object obj) { var type = obj.GetType(); // 遍历所有公开实例属性 foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { // 属性标记了IgnoreAttribute,设为默认值 if (property.GetCustomAttribute<IgnoreAttribute>() != null) { property.SetValue(obj, GetDefaultValue(property.PropertyType)); continue; } // 递归处理嵌套对象(排除值类型和字符串) var propertyValue = property.GetValue(obj); if (propertyValue == null || IsValueTypeOrString(property.PropertyType)) continue; // 处理集合类型,遍历每个元素 if (propertyValue is IEnumerable enumerable) { foreach (var item in enumerable) { if (item != null) RecursivelyProcessObject(item); } } else { RecursivelyProcessObject(propertyValue); } } } // 判断是否是值类型或字符串 private static bool IsValueTypeOrString(Type type) { return type.IsValueType || type == typeof(string); } // 获取指定类型的默认值 private static object GetDefaultValue(Type type) { return type.IsValueType ? Activator.CreateInstance(type) : null; } }
3. 关键注意事项
- 序列化配置兼容:如果项目用Newtonsoft.Json,把
JsonSerializer替换为JsonConvert即可,核心逻辑不变。 - 性能优化:反射遍历会有开销,高并发场景可以缓存类型的属性信息(比如预先记录哪些属性带IgnoreAttribute),避免每次重复反射。
- 复杂类型处理:上述代码已支持嵌套对象和简单集合,若需处理字典、泛型集合等复杂类型,可在递归方法中添加对应分支。
- 流操作规范:务必重置流的
Position为0,否则后续中间件或框架无法正确读取流内容。
内容的提问来源于stack exchange,提问作者Mohammad Taherian
相关产品推荐
相关产品推荐

