如何在ASP.NET Core WebAPI中实现JSON手动反序列化过滤器
解决ASP.NET Core WebAPI自定义JSON到指定类型的转换过滤器
针对你需要的场景——传入JSON结构和目标类型完全不匹配,要通过自定义过滤器显式转换并绑定到控制器参数,我来给你一步步实现方案:
1. 实现自定义Action过滤器
我们需要继承ActionFilterAttribute,在OnActionExecuting阶段拦截请求,读取原始JSON并转换为指定类型,再替换到Action的参数集合中。
代码示例:
using Microsoft.AspNetCore.Mvc.Filters; using System.Text.Json; public class MyFilterAttribute : ActionFilterAttribute { private readonly Type _targetType; public MyFilterAttribute(Type targetType) { _targetType = targetType ?? throw new ArgumentNullException(nameof(targetType)); } public override async Task OnActionExecutingAsync(ActionExecutingContext context, CancellationToken cancellationToken) { // 确保请求体可重复读取(ASP.NET Core中请求体默认只能读一次) context.HttpContext.Request.EnableBuffering(); // 读取原始JSON内容 using var reader = new StreamReader(context.HttpContext.Request.Body, leaveOpen: true); var rawJson = await reader.ReadToEndAsync(cancellationToken); // 重置流位置,避免后续中间件无法读取请求体 context.HttpContext.Request.Body.Position = 0; try { // 核心转换逻辑:将原始JSON转为目标类型实例 var convertedModel = ConvertJsonToTargetType(rawJson, _targetType); // 定位控制器方法中对应的参数(按类型匹配,可扩展为按参数名称匹配) var targetParam = context.ActionDescriptor.Parameters.FirstOrDefault(p => p.ParameterType == _targetType); if (targetParam != null) { // 替换Action参数集合中的值,让控制器方法拿到转换后的实例 context.ActionArguments[targetParam.Name] = convertedModel; } } catch (Exception ex) { // 转换失败时返回标准化错误响应 context.Result = new BadRequestObjectResult(new { Code = "JsonConversionFailed", Message = $"JSON转换为{_targetType.Name}失败:{ex.Message}" }); } await base.OnActionExecutingAsync(context, cancellationToken); } private object ConvertJsonToTargetType(string rawJson, Type targetType) { // 这里替换为你的自定义映射逻辑,示例处理结构完全不同的JSON using var jsonDoc = JsonDocument.Parse(rawJson); var rootElement = jsonDoc.RootElement; // 示例:传入JSON是{"LegacyId":"123","OldName":"test"},目标类型MyType是{"Id":123,"Name":"test"} if (targetType == typeof(MyType)) { return new MyType { Id = rootElement.GetProperty("LegacyId").GetInt32(), Name = rootElement.GetProperty("OldName").GetString() }; } // 如果有多个类型需要转换,建议抽离为单独的映射服务,比如用AutoMapper批量处理 // 示例AutoMapper用法: // var mapper = context.HttpContext.RequestServices.GetRequiredService<IMapper>(); // return mapper.Map(rootElement, targetType); throw new NotSupportedException($"暂不支持转换到类型{targetType.Name}"); } }
2. 在控制器方法中使用过滤器
完全按照你期望的方式标记即可,注意确保控制器方法的参数类型和过滤器指定的typeof(MyType)一致:
using Microsoft.AspNetCore.Mvc; [ApiController] [Route("api/[controller]")] public class DemoController : ControllerBase { [HttpPost] [MyFilter(typeof(MyType))] public IActionResult Post(MyType model) { // 这里的model已经是转换后的实例,可直接使用 return Ok(new { Status = "Success", Data = model }); } } // 示例目标类型 public class MyType { public int Id { get; set; } public string Name { get; set; } }
3. 关键注意事项
- 请求体复用:必须调用
EnableBuffering()并重置流位置,否则后续的日志、授权等中间件会无法读取请求体。 - 转换逻辑扩展:如果涉及多个类型的转换,建议把映射逻辑抽离为独立的服务(比如
IJsonTypeConverter),通过依赖注入注入到过滤器中,避免过滤器代码臃肿。 - 参数匹配优化:如果控制器方法中有多个同类型参数,可以在过滤器构造函数中增加参数名称参数,精准定位要替换的参数。
- 性能与内存:使用
JsonDocument时要注意及时释放(示例中用了using),避免内存泄漏;高并发场景下可以考虑缓存映射配置提升性能。
内容的提问来源于stack exchange,提问作者Jake Shakesworth
相关产品推荐
相关产品推荐

