C# 如何在自定义ActionFilter中根据类名字符串做类型转换?
动态实现枚举匹配DTO的JSON转换逻辑
核心实现思路
要完成根据MetricTypes枚举值匹配同名DTO的转换,不需要提前硬编码每一种枚举对应的类型转换逻辑,只需要两步核心操作:
- 在过滤器初始化阶段,缓存枚举值对应的DTO类型实例,避免运行时反复查找类型的性能损耗
- 调用Newtonsoft.Json自带的非泛型反序列化重载,传入缓存的Type对象完成转换
关键代码实现
修正后的完整过滤器代码如下:
public class CaptureMetrics : ActionFilterAttribute { private readonly string _metricType; private readonly Type _targetDtoType; public CaptureMetrics(MetricTypes metricType) { _metricType = metricType.ToString(); // 从DTO所在的程序集中,查找和枚举名称完全一致的类作为目标类型 _targetDtoType = typeof(ApiMetric).Assembly.GetTypes() .FirstOrDefault(t => t.Name == _metricType); // 启动阶段就校验类型是否存在,避免运行时出错 if (_targetDtoType == null) { throw new InvalidOperationException($"未找到与指标类型{_metricType}同名的DTO定义"); } } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { try { // 开启请求体缓冲,支持流重复读取,不影响后续控制器的模型绑定流程 context.HttpContext.Request.EnableBuffering(); var reqPayload = context.HttpContext.Request.Body; // 重置流位置到开头,从头读取请求内容 reqPayload.Position = 0; string strJson; // 读取请求体时设置leaveOpen=true,避免流被提前关闭 using (var sr = new StreamReader(reqPayload, leaveOpen: true)) { strJson = await sr.ReadToEndAsync(); } // 读完之后再次重置流位置,留给后续模型绑定流程使用 reqPayload.Position = 0; // 带Type参数的非泛型反序列化,直接返回对应DTO类型的实例 var obj = JsonConvert.DeserializeObject(strJson, _targetDtoType); // 后续可直接使用obj做指标采集上报逻辑 } catch (Exception) { // 补充异常日志记录逻辑 throw; } finally { await next(); } } } public enum MetricTypes { ApiMetric } // 与枚举同名的DTO定义 public class ApiMetric { // 实体属性定义 }
容易踩的坑说明
- 不要直接使用
Type.GetType(_metricType)查找类型:该方法默认只会在当前执行程序集、系统程序集中搜索类型,直接传入短类名无法定位到项目内的DTO类,会返回null。在构造阶段一次性扫描程序集匹配同名类型,能在应用启动时就发现枚举和DTO不匹配的配置错误,比运行时抛异常更易排查。 - 必须处理请求流的位置问题:ASP.NET Core的请求体是单向只读流,一旦在过滤器中读取到流末尾,后续控制器的
[FromBody]参数绑定会因为读不到流内容返回null。通过EnableBuffering开启请求体缓冲,每次读写完后把流位置重置到开头,就能兼顾过滤器的内容读取和正常的请求处理流程。
内容的提问来源于stack exchange,提问作者Kgn-web
相关产品推荐
相关产品推荐

