This article explains how to create custom filter attribute in ASP.NET MVC. General purpose of the action and result filters are .NET attributes, derived from FilterAttibute, that also implements IActionFilter or IResultFilter, or both. Also there are some good examples of creating log filters, which are also quite demanded in ASP.NET MVC software development.
So, this article I’d like to tell you about creating your custom filter attribute in ASP.NET MVC (If you're looking for asp.net mvc development services, check our services page). General purpose action and result filters are .NET attributes, derived from FilterAttibute, that also implement IActionFilter or IResultFilter, or both. It’s easier to derive a subclass of the built-in ActionFilterAttibute (it is implemented in both interfaces). There are four methods you can implement:
ASP.NET MVC Methods
Method | When called | Special things |
OnActionExecuting() | Before the action runs | You can prevent execution of the action method by assigning an ActionResult to filterContext.Result. You can inspect and edit filterContext. ActionParameters, the parameters that will be used when calling the action method. |
OnActionExecuted() | After the action runs | You can obtain details of any exception thrown by the action method from filterContext.Exception, and optionally mark it as “handled” by setting filterContext.ExceptionHandled = true. You can inspect or change the ActionResult using filterContext.Result. |
OnResultExecuting() | Before the ActionResult is executed | You can inspect (but not change) the ActionResult using filterContext.Result. You can prevent its execution by setting filterContext.Cancel=true. |
OnResultExecuted() | After the ActionResult is executed | You can obtain details of any exception thrown by the ActionResult from filterContext.Exception, and optionally mark it as “handled” by setting filterContext.ExceptionHandled=true. You can inspect (but not change) the ActionResult using filterContext.Result. |
ASP.NET MVC Filter Sample
So simple filter looks like:
public class ShowMessageAttibute : ActionFilterAttribute
{
public string Message { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write("Before action:" + Message);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write("After action:" + Message);
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Write("Before result:" + Message);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Write("After result:" + Message);
}
}
And you can use it as:
[ShowMessageAttibute(Message = "Hello world!")]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
}
As a result you’ll see all messages which we added in filter. It was really easy, isn’t it?
Now I’ll create more useful filter. This filter will save all site's activity for action method, IP, action and controller names and exception if it’ll be (I’ll use log4net lib for it but you can use any other technology for it ADO.NET Entity Framework, NHibernate, etc).
So first of all I created ASP.NET MVC Web Application in MS VS 2009. After this I added LogsRequestsAttribute.cs and added following code:
public class LogsRequestsAttribute : ActionFilterAttribute, IActionFilter
{
private static ILog log = LogManager.GetLogger(typeof(LogsRequestsAttribute));
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
ThreadPool.QueueUserWorkItem(delegate
{
try
{
string message =
string.Format(
"Action= \"{0}\", Controller= \"{1}\", IPAddress= \"{2}\"" +
"TimeStamp= \"{3}\"",
filterContext.RouteData.Values["action"] as string,
filterContext.Controller.ToString(),
filterContext.HttpContext.Request.UserHostAddress,
filterContext.HttpContext.Timestamp);
log.Info(message);
}
finally
{
}
});
}
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
ThreadPool.QueueUserWorkItem(delegate
{
try
{
string message = filterContext.Exception.Message;
log.Info(message);
}
finally
{
}
});
}
}
}
I don’t explain how you should add and configurate log4net in your app because it’s very simp-ly and it isn’t a goal of this article). In code I used ThreadPool object because user don’t have to wait for logging. So that’s all! Now we can use this filter in our controller or in any action:
[HandleError]
[LogsRequestsAttribute]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
throw new Exception("Error!");
return View();
}
}
If you add filter to controller, it’ll be called for all action in it). I specially throw axception in AboutAction to check this filter for exception. As a result in log file we can see:
Logging successfully initialized
Action= "Index", Controller= "MvcLogApp.Controllers.HomeController", IPAddress= "127.0.0.1"TimeStamp= "15.09.2009 15:56:01"
Action= "About", Controller= "MvcLogApp.Controllers.HomeController", IPAddress= "127.0.0.1"TimeStamp= "15.09.2009 15:56:06"
Error!
So as we see it works and it really simple. (BTW: you can add information in ViewData object in filter and use it on view). You can download code here.
Hope it’ll help somebody!
Artyom G, .NET team, Binary Studio