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

如何重构ASP.NET Core控制器中重复的日志代码,提升可复用性

代码重构方案

针对你需要复用控制器日志逻辑的需求,有两种常用的落地方案,可根据你的实际使用场景选择:

方案一:抽取通用控制器基类(适配需要在Action中使用userId、path打自定义日志的场景)

这是改动最小的方案,所有需要用到该日志逻辑的控制器只要继承该基类即可,无需重复编写初始化逻辑:

// 先定义通用基类
public class BaseController<T> : Controller where T : Controller
{
    protected readonly ILogger<T> Logger;
    protected string UserId;
    protected string RequestPath;

    public BaseController(ILogger<T> logger)
    {
        Logger = logger;
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        base.OnActionExecuting(context);
        UserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
        RequestPath = $"{context.HttpContext.Request.Path.Value}[{context.HttpContext.Request.Method}]";
        Logger.LogDebug($"{RequestPath} started. User(id): {UserId}.");
    }
}

// 业务控制器继承使用
public class HomeController : BaseController<HomeController>
{
    public HomeController(ILogger<HomeController> logger) : base(logger)
    {
    }

    // 你的业务Action
    public IActionResult Approve()
    {
        // 直接用基类的属性打日志即可,和你之前的使用习惯一致
        Logger.LogInformation($"{RequestPath} Was approved for User(id): {UserId}.");
        return Ok();
    }
}

public class AccountController : BaseController<AccountController>
{
    public AccountController(ILogger<AccountController> logger) : base(logger)
    {
    }

    // 其他业务逻辑
}

方案二:使用Action过滤器(适配仅需要统一打入口日志,不需要在Action中访问userId、path的场景)

该方案完全将日志逻辑和控制器解耦,符合单一职责原则,不需要修改现有控制器的继承结构:

// 定义日志过滤器
public class RequestLoggingFilter : IActionFilter
{
    private readonly ILogger<RequestLoggingFilter> _logger;

    public RequestLoggingFilter(ILogger<RequestLoggingFilter> logger)
    {
        _logger = logger;
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var user = context.HttpContext.User;
        var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
        var path = $"{context.HttpContext.Request.Path.Value}[{context.HttpContext.Request.Method}]";
        _logger.LogDebug($"{path} started. User(id): {userId}.");
        
        // 如果还是需要在Action中访问这两个值,可以存入HttpContext.Items
        context.HttpContext.Items["UserId"] = userId;
        context.HttpContext.Items["RequestPath"] = path;
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // 还可以在这里扩展Action执行完成的日志逻辑
    }
}

注册方式:

  • 如果要全局生效:在Program.cs中添加builder.Services.AddControllers(options => options.Filters.Add<RequestLoggingFilter>());
  • 如果只要部分控制器生效:在对应控制器上加[TypeFilter(typeof(RequestLoggingFilter))]特性即可

补充说明:原示例代码存在笔误,HomeController的构造函数中注入的Logger泛型参数写成了AccountController,使用基类方案可以避免这类复制粘贴导致的错误。

内容的提问来源于stack exchange,提问作者Mister D

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 15:15:01