Подтвердить что ты не робот

Как перехватить все вызовы методов контроллера ASP.NET WebApi с перехватом Ninject для ведения журнала?

Наша компания имеет необходимость регистрировать определенные вещи каждый раз, когда вызывается один из наших методов действий наших контроллеров ASP.NET WebApi. Поскольку мы используем Ninject для DI прямо сейчас, мы также хотели бы использовать его для этой цели. Это то, что я пробовал до сих пор.

У меня есть Ninject, Ninject.Extensions.Interception и Ninject.Extensions.Interception.DynamicProxy, установленный через NuGet, и у меня есть следующий модуль

public class InterceptAllModule : InterceptionModule
{
    public override void Load()
    {
        Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
    }
}

Где TimingInterceptor

public class TimingInterceptor : SimpleInterceptor
{
    readonly Stopwatch _stopwatch = new Stopwatch();
    protected override void BeforeInvoke(IInvocation invocation)
    {
        _stopwatch.Start();
    }

    protected override void AfterInvoke(IInvocation invocation)
    {
        _stopwatch.Stop();
        string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
        Log.Info(message + "\n");
        _stopwatch.Reset();
    }
}

Теперь, когда я пытаюсь подключить модуль с ядром ninject и запускаю свой сайт

var kernel = new StandardKernel(new InterceptAllModule());

Однако, всякий раз, когда есть вызов, входящий в один из методов действия, он выдает сообщение об ошибке

Cannot instantiate proxy of class: MyApiController.

Может ли кто-нибудь с опытом указать, что я делаю неправильно? Спасибо.

4b9b3361

Ответ 1

Обновление

Таким образом, используя ваш код и Remo превосходный момент в том, что нужно, чтобы методы действий были виртуальными и помещались в пустой конструктор по умолчанию (просто чтобы успокоить динамический прокси, сохранить еще один конструктор), у меня есть оба фильтр действия и подход к перехвату.

Я бы сказал, что, поскольку он стоит, ваш код будет перехватывать потенциально нежелательные методы на ApiController, поэтому вам, вероятно, также потребуется поместить некоторый код в место, чтобы отфильтровать их, например. ExecuteAsync и Dispose.

Мой единственный момент - это производительность. Огромный отказ от ответственности - это всего лишь очень простые тесты (каждый раз при использовании подхода фильтра действий каждый раз, чтобы регистрировать статистику), я предлагаю вам сделать свой (!)... но используя перехватчик DynamicProxy, я получал время около 4 миллисекунд за запрос на запрос

[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]

Комментируя код перехвата и используя фильтр действия, я получал субмиллисекундную производительность:

[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]

Это зависит от вас, действительно ли это проблема или проблема, но я подумал, что хочу указать на это.

Предыдущий ответ

Вы управляли с помощью ActionFilters? Это естественная точка расширения для АОП при действии MVC.

Если бы вас интересовали методы, отличные от фактического действия на контроллере, я бы понял, но я думал, что все равно опубликую предложение.

Вдохновленный Повторяются ли ActionFilterAttributes по потокам? Как это работает? и Измерять время, вызывая действия ASP.NET MVC Controller.

Обновлен, чтобы показать исключение таймера при пометке метода. Вдохновение из основной структуры WebApi специально AllowAnonymousAttribute и AuthorizeAttribute

Зарегистрируйте глобально, чтобы отслеживать все действия:

GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());

Тогда:

public class TimingActionFilter : ActionFilterAttribute
{
    private const string Key = "__action_duration__";

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (SkipLogging(actionContext))
        {
            return;
        }

        var stopWatch = new Stopwatch();
        actionContext.Request.Properties[Key] = stopWatch;
        stopWatch.Start();
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
        {
            return;
        }

        var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
        if(stopWatch != null)
        {
            stopWatch.Stop();
            var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
            Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
        }

    }

    private static bool SkipLogging(HttpActionContext actionContext)
    {
        return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
                actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
    }
}

и

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{

}

Теперь вы можете исключить глобальный фильтр, используя:

public class ExampleController : ApiController
{
    // GET api/example
    [NoLog]
    public Example Get()
    {
       //
    }
}

Ответ 2

Для тех, кто все еще скрывается, причиной, по которой я хотел использовать Ninject, было то, что я мог вставить регистратор (или что-то еще) в перехватчик, но я хотел перехватить все действия.

Отметка ответа идеальна, но вместо того, чтобы регистрироваться глобально с помощью

GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());

свяжите свой фильтр с Ninject, используя

Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).

Вам нужно создать соответствующий contructor в классе TimingActionFilter.