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

如何在WCF中实现类似Web API的请求日志记录及入库功能

为什么直接复用Web API的[LogApiRequest]特性在WCF中不生效

你之前实现的Web API日志方案是基于ASP.NET Web API专属的Action Filter拦截管道开发的,WCF的请求处理链路、扩展点机制和Web API完全独立,没有兼容Web API过滤器的默认逻辑,因此直接在WCF方法上标记原有特性不会触发日志拦截逻辑。

WCF落地同等日志能力的实现步骤

你不需要重写已有的日志字段组装、数据库写入逻辑,只需要把触发日志记录的拦截层替换为WCF原生支持的扩展点即可,最终使用方式和之前Web API完全一致,只需要在需要记录日志的方法上打标记。

  • 第一步:保留原有[LogApiRequest]特性标记和日志入库核心逻辑
    你之前写的日志内容拼装、写入数据库的代码可以100%复用,不需要做任何修改。
  • 第二步:实现WCF自定义操作调用器,完成请求前后的拦截
    WCF中要拿到接口方法元数据、入参、返回值、执行耗时、异常信息,最适配的扩展点是IOperationInvoker接口,示例实现代码如下:
public class LogApiRequestInvoker : IOperationInvoker
{
    private readonly IOperationInvoker _originalInvoker;
    private readonly bool _isLogEnabled;

    public LogApiRequestInvoker(IOperationInvoker originalInvoker, MethodInfo methodInfo)
    {
        _originalInvoker = originalInvoker;
        // 判断当前方法是否标记了[LogApiRequest]特性,和Web API的判断逻辑一致
        _isLogEnabled = methodInfo.GetCustomAttributes(typeof(LogApiRequestAttribute), false).Any();
    }

    public object[] AllocateInputs()
    {
        return _originalInvoker.AllocateInputs();
    }

    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        if (!_isLogEnabled)
        {
            // 未打日志标记的方法,直接走原有逻辑
            return _originalInvoker.Invoke(instance, inputs, out outputs);
        }

        var stopwatch = Stopwatch.StartNew();
        var logModel = new ApiLogModel();
        // 这里填充你之前Web API日志里的字段:请求时间、方法名、入参、客户端IP等
        logModel.RequestTime = DateTime.Now;
        logModel.MethodName = methodInfo.Name;
        logModel.RequestParams = JsonConvert.SerializeObject(inputs);
        
        object result;
        try
        {
            result = _originalInvoker.Invoke(instance, inputs, out outputs);
            logModel.ResponseContent = JsonConvert.SerializeObject(result);
            logModel.IsSuccess = true;
        }
        catch (Exception ex)
        {
            logModel.IsSuccess = false;
            logModel.ExceptionMsg = ex.ToString();
            outputs = new object[0];
            throw;
        }
        finally
        {
            stopwatch.Stop();
            logModel.ExecuteDuration = stopwatch.ElapsedMilliseconds;
            // 直接复用你之前写的日志写入数据库的方法
            ClsLogDal.WriteLogToDb(logModel);
        }

        return result;
    }

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
    {
        // 异步方法的拦截逻辑和上面同步逻辑对齐即可,字段填充逻辑完全一致
        return _originalInvoker.InvokeBegin(instance, inputs, callback, state);
    }

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        return _originalInvoker.InvokeEnd(instance, out outputs, result);
    }

    public bool IsSynchronous => _originalInvoker.IsSynchronous;
}
  • 第三步:实现WCF操作行为特性,把自定义调用器注入到WCF管道
    实现IOperationBehavior接口的特性类,用来替换WCF默认的操作调用器,注入后WCF管道就会自动识别你打在方法上的日志标记:
[AttributeUsage(AttributeTargets.Method)]
public class LogApiRequestBehaviorAttribute : Attribute, IOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        // 替换为我们带日志逻辑的调用器
        dispatchOperation.Invoker = new LogApiRequestInvoker(dispatchOperation.Invoker, operationDescription.SyncMethod ?? operationDescription.TaskMethod);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}
  • 第四步:注册行为并使用
    你有两种注册方式可选:
    1. 方法级注册:和你之前Web API的用法完全一致,直接在WCF服务实现类的对应方法上保留原有[LogApiRequest]标记即可,不需要修改方法内部逻辑:
public class NadlanService : INadlanService
{
    [LogApiRequest] 
    public NadlanData GetNadlanData(decimal Id, KodFamilyEnum KodFamily)
    {
        ClsDalByTz objByTz = new ClsDalByTz();           
        return objByTz.getDataFromMF(Id, DateTime.Now.Year - 5, (decimal)KodFamily);  
    }
}
  1. 全局注册:如果需要给所有WCF方法默认开启日志,可以把上述行为升级为服务行为(IServiceBehavior),在服务启动配置中全局注册,不需要给每个方法单独打标记。
注意事项

如果你的WCF是REST风格服务,也可以选择实现IDispatchMessageInspector接口做消息级拦截,但IOperationInvoker的实现更贴近你之前Web API过滤器的使用体验,拿到的参数、返回值都是已经完成反序列化的强类型对象,不需要额外解析SOAP/HTTP消息体,复用原有日志逻辑的成本最低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 17:27:35