You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:28:33