autofac / Autofac.Extras.DynamicProxy

Interceptor and decorator support for Autofac IoC via Castle DynamicProxy
MIT License
106 stars 33 forks source link

Intercept controllers methods ASP 5 #6

Closed IC3Q closed 7 years ago

IC3Q commented 8 years ago

I'm using ASP NET 5 in rc1-final version. My autofac-related dependencies are:

"Autofac.Extensions.DependencyInjection": "4.0.0-rc1-177",
"Autofac.Extras.DynamicProxy": "4.0.0-beta8-231",

Is there a possibility to intercept controller's methods? I've tried with some 'normal' classes and everything worked fine. Problem occurs when I try to do stuff with MVC controllers. Tried already:

    public interface IMyInterface
    {
        IActionResult Get(int id);
    }

    [Intercept("log-calls")]
    public class MyController : Controller, IMyInterface
    {
        public IActionResult Get(int id)
        {
            return new JsonResult(new SomeClass(id));
        }
    }

    public class CallLogger : IInterceptor
    {
        public void Intercept(IInvocation invocation)
        {
            //breakpoint here
            invocation.Proceed();
        }
    }

And part of ConfigureServices:

    ...
    services.AddMvc();
    ...
    builder.RegisterType<MyController>()
        .As<IMyInterface>()
        .EnableInterfaceInterceptors();

    builder.Register(c => new CallLogger())
        .Named<IInterceptor>("log-calls");

    builder.Populate(services);

    var container = builder.Build();

    return container.Resolve<IServiceProvider>();

But nothing worked. Am I missing something or is it not possible to do that now?

tillig commented 7 years ago

When ASP.NET resolves the controller, it resolves it by class, not by interface. It's basically doing:

lifetimeScope.Resolve<MyController>()

Which means you need to intercept the class, not an interface, and you need to register after you do builder.Populate(services) to make sure your interceptor registration takes precedence over things that were registered as part of services.AddMvc().

Try something like...

services.AddMvc();
builder.Populate(services);
builder.RegisterType<MyController>().EnableClassInterceptors();
builder.Register(c => new CallLogger()).Named<IInterceptor>("log-calls");

return new AutofacServiceProvider(builder.Build());

Keep in mind that you can only intercept virtual methods so your controller may have to be updated to accommodate for that.

I'm going to close the ticket for now, but if you find it doesn't work then we can re-open if needed.

rolandroos commented 4 years ago

Yes, that works brililiant on the intersception part. Hoever, you want in your intersception get the custom attributes, something like the class, or method one: [SomeActionAtrribute (Id=10)] public IActionResult Index() { return View(); } that fails:

public void Intercept(IInvocation invocation) { var methodAttributes = invocation.Method.GetCustomAttributes(false); SomeActionAtrribute action=(SomeActionAtrribute )methodAttributes.Where(a => a.GetType() == typeof(SomeActionAtrribute )).SingleOrDefault(); if (action!= null) {....

As the invocation methods will be something like Controllers.OnAction_Executing(), and never Index(). So, you want find your custom attribributes. That doesnt work, and if it does, you get called 6-7-8 times, as multiple actions go off.

Now, gladdly, in .NET core, there is a ActionFilterAttribute. public class SomeActionAtrribute : ActionFilterAttribute { public long Id { get; set; } public override void OnActionExecuting(ActionExecutingContext context) { ControllerActionDescriptor actionDescriptor = (ControllerActionDescriptor)context.ActionDescriptor; var controllerName = actionDescriptor.ControllerName; var actionName = actionDescriptor.ActionName; var parameters = actionDescriptor.Parameters; var fullName = actionDescriptor.DisplayName;

        ...Do your custom intersception here...
    }
}

so, do intersception everywhere on your model with some intersceptor pattern of e.g. autofac/Castle etc, but on your .Net Core controllers use ActionFilterAttributes.