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

如何在ASP.NET Core中为Action返回的JSON追加额外性能数据

回答

ASP.NET Core完全支持该需求,且不需要使用全量生效的中间件。针对MVC API接口场景,Action过滤器(Action Filter) 是适配度远高于中间件的实现方案,核心优势是可以精准控制生效范围,且在响应序列化前介入处理,不需要操作原始响应流,实现成本和性能表现都更优。

为什么不推荐用中间件实现

中间件运行在ASP.NET Core最外层请求管道,会拦截所有流量,包括静态资源、健康检查、SignalR连接等非接口请求。如果要在中间件层追加JSON字段,需要先开启响应缓冲、反序列化响应内容、修改后再重新序列化回写,不仅适配成本高,还会带来额外的性能开销,很不划算。

Action过滤器实现方案

过滤器是ASP.NET Core MVC管道内置的组件,天然支持按全局、控制器、Action三级配置生效范围,只会进入MVC执行流程的请求才会被过滤器处理,不会干扰其他非接口请求。

核心实现逻辑

  1. 在目标Action执行前启动Stopwatch计时
  2. 等待Action执行完成,捕获返回的对象结果(和描述中「所有接口均返回对象类型」场景完全匹配)
  3. 向返回对象追加milliseconds耗时字段
  4. 交给框架原有的JSON序列化逻辑输出响应即可

实现代码示例

using System.Dynamic;
using System.Diagnostics;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

public class AppendMillisecondsFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 启动计时
        var stopwatch = Stopwatch.StartNew();
        // 执行目标Action逻辑
        var executedContext = await next();
        stopwatch.Stop();

        // 仅处理对象类型返回结果,跳过其他类型结果(比如重定向、文件返回等)
        if (executedContext.Result is ObjectResult objectResult && objectResult.Value != null)
        {
            // 如果你所有接口都用了统一的返回包装基类,直接转基类赋值即可,不需要动态对象
            // 示例:((ApiResult)objectResult.Value).Milliseconds = stopwatch.ElapsedMilliseconds;
            
            // 无统一返回基类的场景,用动态对象追加字段
            var expandoDict = new ExpandoObject() as IDictionary<string, object>;
            // 拷贝原有对象的所有公共属性
            foreach (var prop in objectResult.Value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                expandoDict[prop.Name] = prop.GetValue(objectResult.Value);
            }
            // 追加耗时字段
            expandoDict["milliseconds"] = stopwatch.ElapsedMilliseconds;

            // 替换返回结果,后续框架会自动序列化为JSON
            executedContext.Result = new ObjectResult(expandoDict)
            {
                StatusCode = objectResult.StatusCode
            };
        }
    }
}

生效范围控制

可以根据需求灵活选择过滤器的注册方式,精准匹配10个目标控制器的接口,完全不影响其他请求:

  • 全局注册后内部判断:在过滤器中通过context.ActionDescriptor获取当前请求的控制器名称、命名空间,仅匹配ControllerN命名规则的请求才执行追加逻辑
  • 基类标记:让10个目标控制器继承同一个空基类,把过滤器标记在基类上,所有子类自动生效
  • 特性标记:自定义[AppendTiming]特性,将过滤器作为特性打在需要的控制器或Action上

全局注册过滤器的代码如下:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(options =>
{
    options.Filters.Add<AppendMillisecondsFilter>();
});

可选调整

  • 如果需要统计包含模型绑定、其他过滤器执行在内的更完整耗时,可以把计时启动逻辑移到资源过滤器(Resource Filter)中,扩大统计覆盖范围
  • 如果接口返回值存在嵌套结构、特殊字段命名规则,可以在属性拷贝逻辑中按需适配,不需要调整业务接口代码

内容的提问来源于stack exchange,提问作者Big boy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:20:03