How to intercept all the ASP.NET WebApi controller action methods calls with Ninject interception for logging? - asp.net-mvc

Our company has the need to log certain things each time one of our action methods of our ASP.NET WebApi controllers gets called. Since we use Ninject for the DI right now, we'd like to use it also for this purpose. This is what I have tried so far.
I have Ninject, Ninject.Extensions.Interception and Ninject.Extensions.Interception.DynamicProxy installed through NuGet and I have the following module
public class InterceptAllModule : InterceptionModule
{
public override void Load()
{
Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
}
}
Where TimingInterceptor is
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();
}
}
Now, when I try to hook the module up with ninject kernel, and run my site
var kernel = new StandardKernel(new InterceptAllModule());
However, whenever there is a call coming in to one of the action method, it throws an error saying
Cannot instantiate proxy of class: MyApiController.
Could someone with experience point out what I'm doing wrong please? Thanks.

Update
So using your Code and Remo's excellent point about needing the action methods to be virtual and putting in an empty default constructor (just to placate dynamic proxy, keep your other constructor still) I have got both the action filter and the interception approach working.
I would say that as it stands your code will intercept potentially unwanted methods on the ApiController so you will probably also need to put some code in place to filter these out e.g. ExecuteAsync and Dispose.
My only other point is performance. Huge disclaimer these are just very basic tests (using the action filter approach each time to log the stats), I invite you to do your own(!)... but using the DynamicProxy interceptor I was getting a time of around 4 milliseconds per get request
[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Commenting out the Interception code and using an Action filter I was getting sub millisecond performance:
[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
It is up to you whether this is actually an issue or concern but I thought I would point this out.
Previous Response
Have you rulled out using ActionFilters? This is the natural extension point for AOP on an MVC action.
If you were interested in methods other than the actual action on the controller then I would understand but I thought I would post a suggestion anyway.
Inspired by Are ActionFilterAttributes reused across threads? How does that work? and Measure Time Invoking ASP.NET MVC Controller Actions.
Updated to show the exclusion of the timer when method tagged. Inspiration from core WebApi framework specifically AllowAnonymousAttribute and AuthorizeAttribute
Register this globally so that all actions are monitored by this:
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Then:
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();
}
}
And
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{
}
Now you can exclude the global filter using:
public class ExampleController : ApiController
{
// GET api/example
[NoLog]
public Example Get()
{
//
}
}

For anyone still lurking, the reason I wanted to use Ninject was so I could inject a logger (or anything else) into the interceptor, but I wanted to intercept all actions.
Mark's answer is perfect, but instead of registering globally using
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
bind your filter with Ninject using
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).
You'll need to create an appropriate contructor in the TimingActionFilter class.

Related

asp.net mvc web api 2 custom logger + controller lifecycle

story:I have following component in my asp.net mvc 5 web api project(using ef code first)
myBaseController : base api controller class
myLogger : my costume logger class
class myBaseController { MyLogger Logger; }
class MyLogger
{
public addLog(Log log)
{
db.logs.add(log);
db.saveChanges();
}
}
Each api controller use base.logger.addLog(new log("somethings")) and the log committed to database immediately.
Problem: I want MyLogger class be able to collect all log in memory and some where add theme to database at once.
I put it in
protected override void Dispose(bool disposing)
{
logger.AddRange(_logs);
}
but it doesn't work,it seems objects are not available in this method or grabbed from memory
How it should be handled?
You don't need to collect records in memory separately. Because EF will do that for you. For instance db.logs.add means, please add it to memory. But when you call SaveChanges it will commit all of the in-memory collected data to the database at once. So you will need to implement the unit of work pattern (UoW). By using this pattern, you will have only one context per request and then at the end of the request you will call the SaveChanges method once. You shouldn't have multiple SaveChanges everywhere in your codes, this is your problem right now. Your addLog method calls SaveChanges, your other methods call SaveChanges and so on.
More info from Microsoft: Implementing the Repository and Unit of Work Patterns in an ASP.NET MVC Application
You need to store your entries in memory during request cycle and at end of request you can save those entries into database
protected void Application_EndRequest()
{
//get entries from memory/session/cache whatever your source to store entries
//do stuff to save entries
}
I normally create ActionFilterAttributes, and place it on action methods where I want to log any activity.
TraceMvcAttribute
public class TraceMvcAttribute : ActionFilterAttribute
{
// IoC container will inject this dependencies.
public IWebUserSession WebUserSession { get; set; }
public IDateTime DateTime { get; set; }
public ITraceListener TraceListener { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var traceLog = new TraceLog
{
Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Action = filterContext.ActionDescriptor.ActionName,
Message = JsonConvert.SerializeObject(filterContext.ActionParameters),
PerformedOn = this.DateTime.Now,
PerformedBy = WebUserSession?.UserName
};
TraceListener.AddTraceLogAsync(traceLog);
base.OnActionExecuting(filterContext);
}
}
TraceApiAttribute
Web API filter is a little bit different from MVC filter. I do not have it in my sample code at Git; in case you might want to take a look at it.
public class TraceApiAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// http://autofac.readthedocs.org/en/latest/integration/webapi.html#standard-web-api-filters-are-singletons
// All filter attributes in Web API are effectively singleton instances that exist
// for the entire lifetime of the application. We must use service location,
// since we need per-request services in your filters.
var requestScope = actionContext.Request.GetDependencyScope();
var datetime = requestScope.GetService(typeof(IDateTime)) as IDateTime;
var webUserSession = requestScope.GetService(typeof(IWebUserSession)) as IWebUserSession;
var traceListener = requestScope.GetService(typeof(ITraceListener)) as ITraceListener;
var traceLog = new TraceLog
{
Controller = actionContext.ControllerContext.ControllerDescriptor.ControllerName,
Action = actionContext.ActionDescriptor.ActionName,
Message = JsonConvert.SerializeObject(actionContext.ActionArguments.Where(x => x.Key != "request").ToList()),
PerformDateTime = datetime.Now,
PerformBy = webUserSession?.UserName
};
traceListener.AddTraceLogAsync(traceLog);
base.OnActionExecuting(actionContext);
}
}
I then view the log history like this -

Accessing ControllerContext from within a Delegate within the Global.asax

Is there any way to gain access to the controller that is currently executing from within Global.asax?
I'd like to design an API with similar syntax to:
MyClass.RegisterComponents().When(IController => /* Some condition */)
Although I could move this code to a place where the controller is in context, I'd like to keep it centralised and portable.
So far, I have been unable to obtain the controller. Any ideas?
I have considered creating a base controller and extending all of my controllers from this base class, however, I'd like to make this library portable with the ability to be installed via NuGet. For this reason I am unable to take this approach.
You can do following in your global.asmx file.
private void Application_BeginRequest(object sender, EventArgs e)
{
string controllerName = Request.RequestContext.RouteData.Values.Where(p => p.Key =="controller").FirstOrDefault(p => p.Key);
}
I found a solution to this. Not a very good one but it solves my problem.
Register a global IActionFilter using an assembly start up method I found on David Ebbo's blog (http://blog.davidebbo.com/2011/02/register-your-http-modules-at-runtime.html).
The global action filter simply stores the action context in the current HttpContext.Items[] collection which is a per request collection.
public class GlobalActionFilter : System.Web.Mvc.IActionFilter {
internal static readonly object ActionExecutedFilterKey =
"__MvcResourceLoaderActionExecutedContext";
internal static readonly object ActionExecutingFilterKey =
"__MvcResourceLoaderActionExecutingContext";
static MvcResourceLoaderGlobalFilter __instance =
new MvcResourceLoaderGlobalFilter();
MvcResourceLoaderGlobalFilter() { }
public void OnActionExecuted(System.Web.Mvc.ActionExecutedContext filterContext) {
System.Web.HttpContext.Current.Items[ActionExecutedFilterKey] =
filterContext;
}
public void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext) {
System.Web.HttpContext.Current.Items[ActionExecutingFilterKey] =
filterContext;
}
public static void RegisterGlobalFilter() {
if (!System.Web.Mvc.GlobalFilters.Filters.Contains(__instance))
System.Web.Mvc.GlobalFilters.Filters.Add(__instance);
}
}
I can then access the context anywhere.

When to commit NHibernate Transaction?

While very familiar to Webforms and Linq, I am a novice to the ASP.NET MVC and NHibernate World. I have been working through a project using Bob Cravens' examples. My application is mostly reads and non-sequential writes, so typically I would not use a transactions. But to implement the Unit-of-Work pattern, all my research including Ayende's blog says I should.
The problem I have is this -
Ninject creates a Session and Opens a Transaction.
Ninject injects repositories into services, and services into controllers.
I make some changes to the properties and children of an object and save on the aggregate root. This calls Transaction.Commit (works fine, as expected)
In another method later in the controller, I try to save a separate object
The second call fails because the transaction is no longer active.
I'm thinking of adding a bool "CommitNeeded" to the UnitOfWork which would be set by my Save() method and conditionally trigger a Commit() on UnitOfWork.Dispose(). Is this a good idea?
Should I remove the transaction infrastructure? Should I change my Commit()'s to Flush()'s?
Any advice that would help fix my anti-pattern would be appreciated.
In response to the comments - I guess I don't mind if they happen together or separate. There are two things going on. The first one changes a "Customer" object, and then saves it. The second makes a logging entry which then calls the same "Save" method.
var Customer = ServiceLayer.GetCustomer(...);
Transmogrify(Customer, Customer.Children, Widgets, ...);
ServiceLayer.Save(Customer)
ServiceLayer.RecordEvent(Customer, "Customer was frobbed")
where LogEvent looks like
public void RecordEvent(Customer customer, int eventTypeId, string description)
{
...
Save(customer);
}
The RecordEvent method has its own "save" because it is called from other controllers that do no data changes. I believe the Save call doesn't belong in either of those places. The question is, where? The Dispose() method of the Service Layer? or a Filter like the other users suggested?
Using ASP.NET MVC, I use an action filter to bind the transaction scope to the controller action execution lifecycle. This works great most of the time, but you have to be cautious not to keep transactions open too long.
public class UnitOfWorkActionFilter : ActionFilterAttribute
{
public IUnitOfWork UnitOfWork { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
UnitOfWork.Start();
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception == null)
{
UnitOfWork.Commit();
}
else
{
UnitOfWork.Rollback();
}
UnitOfWork.Dispose();
base.OnActionExecuted(filterContext);
}
}
In my case I'm using property injection via a custom ControllerActionInvoker to get the IUnitOfWork dependency into the ActionFilterAttribute.
I'm using for that an http module. I get the transaction at the beginning of http request and terminate it on the end of http request :
public class UnitOfWorkModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += context_BeginRequest;
context.EndRequest += context_EndRequest;
}
private void context_BeginRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
instance.Begin();
}
private void context_EndRequest(object sender, EventArgs e)
{
IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
try
{
instance.Commit();
}
catch
{
instance.RollBack();
}
finally
{
instance.Dispose();
}
}
}
My unit of work factory it's just a Func initialized while registering types in IoC container :
public class UnitOfWorkFactory
{
public static Func<IUnitOfWork> GetDefault;
}
Initialization (for my case StructureMap) :
UnitOfWorkFactory.GetDefault = () => container.GetInstance<IUnitOfWork>();
And then you register you in UnitOfWorkModule web.config
<httpModules>
<add name="UnitOfWorkModule" type="UI.UnitOfWorkModule, UI" />
</httpModules>

UnitOfWork in Action Filter seems to be caching

I have an MVC 3 site that uses IoC (Unity), and my model is generated w/ EF4 and POCOs. I am using an action filter to commit my UnitOfWork:
public class UseUnitOfWorkAttribute : ActionFilterAttribute, IActionFilter
{
private readonly IUnitOfWork _unitOfWork;
public UseUnitOfWorkAttribute()
{
_unitOfWork = IoCFactory.Instance.CurrentContainer.Resolve<IUnitOfWork>();
}
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
_unitOfWork.Commit();
}
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
However, even though the Commit() seems to be getting fired, it somehow seems to be caching what it thinks is "dirty".
For example, in my controller, the following gets executed from a service class:
var user = _userRepository.Single(u => u.Id == 2);
user.DateAdded = DateTime.Now;
Whenever I do a fresh build of the solution and hit this controller action, the change is actually committed. However, successive hits to the controller doesn't do anything.
On the other hand, if I put a UnitOfWork in my controller and commit it following the service method call, it works as expected (every time I request the controller action):
public AccountController()
{
_unitOfWork = IoCFactory.Instance.CurrentContainer.Resolve<IUnitOfWork>();
}
public ActionResult Test()
{
var user = _userRepository.Single(u => u.Id == 2);
user.DateAdded = DateTime.Now;
_unitOfWork.Commit();
}
So it definitely seems like some sort of caching is going on, but I can't figure it out what is getting cached -- the UnitOfWork, the ActionFilter, or the repository.
Any ideas what could be going on? And if not, any ideas what else I could do to troubleshoot?
Thanks in advance.
You are initializing your unit of work in the constructor of the action filter which means that it will be injected when the action filter is instantiated. Quote from the ASP.NET MVC 3 release notes:
In previous versions of ASP.NET MVC,
action filters were created per
request except in a few cases. This
behavior was never a guaranteed
behavior but merely an implementation
detail and the contract for filters
was to consider them stateless. In
ASP.NET MVC 3, filters are cached more
aggressively. Therefore, any custom
action filters which improperly store
instance state might be broken.
Make sure the dependency container returns the same instance in all places and rewrite the filter to avoid state caching:
public class UseUnitOfWorkAttribute : ActionFilterAttribute, IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
var unitOfWork = IoCFactory.Instance.CurrentContainer.Resolve<IUnitOfWork>();
unitOfWork.Commit();
}
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
}
}
I would check the lifetime on your repository. That was certainly the culprit in our implementation.

Mock a web service used in an action filter

I have an external-to-my-solution web service that I'm using in an ActionFilter. The action filter grabs some basic data for my MasterPage. I've gone back and forth between using an action filter and extending the base controller class, and decided the action filter was the best approach. Then I started unit testing (Yeah, yeah TDD. Anyway... :D )
So I can't mock (using Moq, btw) a web service in an action filter because I can't inject my mock WS into the action filter, since action filters don't take objects as params. Right? At least that's what I seem to have come to.
Any ideas? Better approaches? I'm just trying to return a warning to the user that if the web service is unavailable, their experience might be limited.
Thanks for any help!
namespace MyProject.ActionFilters
{
public class GetMasterPageData : ActionFilterAttribute
{
public ThatWS ws = new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
try {
DoStuff();
}
catch ( NullReferenceException e ) {
context.Session["message"] = "There is a problem with the web service. Some functionality will be limited.";
}
}
}
}
Here's a quick and dirty approach:
public class GetMasterPageData : ActionFilterAttribute
{
public Func<ISomeInterface> ServiceProvider = () => new ThatWS();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var result = ServiceProvider().SomeMethod();
...
}
}
And in your unit test you could instantiate the action filter and replace the ServiceProvider public field with some mocked object:
objectToTest.ServiceProvider = () => new SomeMockedObject();
Of course this approach is not as clean as the one suggested by #Ryan in the comments section but it could work in some situations.

Resources