We already know that Action Filters are important to modify the behavior of the action. If we want to apply some operations on the action execution then we need to make our own custom action filters which help us to apply the operations on different states. We’ll discuss them in detail here.
Let’s create the custom action filter which will log (print/show) the following details.
- Name of the Controller
- Name of the Action
- Execution Time of the Action Method
- And if there is any error or an exception in the action method, then it will log that exception message and the time of the exception when it occurs.
We have already seen the order of execution of the action filters. But if we want to execute them in a specific order, then we’ll use the Order property of action filter.
Remember
The base class type of all the custom action filters is FilterAttribute and if you want to create your own custom action filter then you need to inherit this class from the base FilterAttribute class and we’ve already seen our different types of action filters actually implement relevant specific interfaces.
- Authorization Filter (implements IAuthorizationFilter)
- Action Filter (implements IActionFilter)
- Result Filter (implements IResultFilter)
- Exception Filter (implements IExceptionFilter)
So we need to implement one of these interfaces as well which depends upon our requirement. If we’re making an authorization filter then we’ll implement IAuthorizationFilter interface and if we’re developing Action Filter then we’ll implement IActionFilter and so on.
But if you want to make Action Filter then you’ve another option as well, make your custom class and just inherit it from ActionFilterAttribute.
Let’s start our journey.
- Make an ASP.NET application [Empty or MVC (depends upon your choice)]
- Now add the folder here ‘ActionFilters’
- Add class ‘LogTimeException’
- And inherit it from FilterAttribute and IActionFilter
- namespace SCMS.ActionFilters
- {
- public class LogTimeException: FilterAttribute, IActionFilter
- {
- public void OnActionExecuting(ActionExecutingContext filterContext)
- {
- throw new System.NotImplementedException();
- }
-
- public void OnActionExecuted(ActionExecutedContext filterContext)
- {
- throw new System.NotImplementedException();
- }
- }
- }
This interface implements 2 methods. We’ve four interfaces (IAuthorizationFilter, IActionFilter and so on) and all of them have different number of abstract methods in it. For more details,
- OnActionExecuting()
It executes before the action is executed.
- OnActionExecuted()
It executes after the action is executed.
And if we remove these 2 methods and inherit our class just with ActionFilterAttribute then it overrides 4 methods.
- OnActionExecuting()
- OnActionExecuted()
- OnResultExecuting()
It executes before the result executes meanings that before rendering the view
- OnResultExecuted()
It executes after the result executes means that after rendering the view.
Plus we need to show the exception messages as well so implement IExceptionFilter.
Now this is what our custom action filter class looks like,
- public class LogTimeException : ActionFilterAttribute, IExceptionFilter
- {
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- base.OnActionExecuting(filterContext);
- }
-
- public override void OnActionExecuted(ActionExecutedContext filterContext)
- {
- base.OnActionExecuted(filterContext);
- }
-
- public override void OnResultExecuting(ResultExecutingContext filterContext)
- {
- base.OnResultExecuting(filterContext);
- }
-
- public override void OnResultExecuted(ResultExecutedContext filterContext)
- {
- base.OnResultExecuted(filterContext);
- }
-
- public void OnException(ExceptionContext filterContext)
- {
- throw new System.NotImplementedException();
- }
- }
Now replace the OnActionExecuting() with,
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- string message = Environment.NewLine + filterContext.ActionDescriptor.ControllerDescriptor.ControllerName +
- " & " + filterContext.ActionDescriptor.ActionName +
- @" = OnActionExecuting " + DateTime.Now.ToString("F") + Environment.NewLine;
- }
The code is already self explanatory, these filterContext statements are showing the name of controller and action.
Here we’ve another approach as well to show the controller names and actions with the help of magic strings.
- string message = filterContext.RouteData.Values["controller"] +
- " & " + filterContext.RouteData.Values["action"] +
- @" = OnResultExecuting " + DateTime.Now.ToString("F") + Environment.NewLine;
It actually taking the controller and action names from route, very simple.
Now, add the Data folder in the project and add the text file to it.
Right-click on your ‘Data’ folder and select ‘New Item’.
Here in this file, we’ll log all the details of our action. So, the final code is,
- using System;
- using System.IO;
- using System.Web;
- using System.Web.Mvc;
-
- namespace SCMS.ActionFilters
- {
- public class LogTimeException : ActionFilterAttribute, IExceptionFilter
- {
- public override void OnActionExecuting(ActionExecutingContext filterContext)
- {
- string message = Environment.NewLine + filterContext.ActionDescriptor.ControllerDescriptor.ControllerName +
- " & " + filterContext.ActionDescriptor.ActionName +
- @" = OnActionExecuting " + DateTime.Now.ToString("F") + Environment.NewLine;
-
-
- Log(message);
- }
-
- public override void OnActionExecuted(ActionExecutedContext filterContext)
- {
- string message = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName +
- " & " + filterContext.ActionDescriptor.ActionName +
- @" = OnActionExecuted " + DateTime.Now.ToString("F") + Environment.NewLine;
-
-
- Log(message);
- }
-
- public override void OnResultExecuting(ResultExecutingContext filterContext)
- {
- string message = filterContext.RouteData.Values["controller"] +
- " & " + filterContext.RouteData.Values["action"] +
- @" = OnResultExecuting " + DateTime.Now.ToString("F") + Environment.NewLine;
- Log(message);
- }
-
- public override void OnResultExecuted(ResultExecutedContext filterContext)
- {
- string message = filterContext.RouteData.Values["controller"] +
- " & " + filterContext.RouteData.Values["action"] +
- @" = OnResultExecuted " + DateTime.Now.ToString("F") + Environment.NewLine;
- Log(message);
- }
-
- public void OnException(ExceptionContext filterContext)
- {
- string message = filterContext.RouteData.Values["controller"] +
- " & " + filterContext.RouteData.Values["action"] +
- " & " + filterContext.Exception.Message + @" " +
- @"= OnResultExecuted " + DateTime.Now.ToString("F") + Environment.NewLine;
- Log(message);
- }
-
- private void Log(string message)
- {
-
- File.AppendAllText(HttpContext.Current.Server.MapPath("~/Data/Data.txt"), message);
- }
- }
- }
And the only thing which is remaining from this discussion is:
filterContext.Exception.Message is used to show the exception message in OnException.
Now, let’s apply the custom action filter on the controller.
- [LogTimeException]
- public class StudentController : Controller
- {
-
- public string Index()
- {
- return "Huy Dear";
- }
-
- public ActionResult Welcome()
- {
- throw new Exception("Exception Occured");
- }
- }
Now it is the time to run the application and execute the controller actions.
After executing the action, open the text file and you’ll see the log in the text file. You can also print this log in the Output Window of Visual Studio.
Just Replace the File.AppendAllText() with Debug.WriteLine(message)
Conclusion
Action Filters are generally used for caching, logging, authorization, exception handling etc. As we can already apply the action filter to actions and controllers as well, similarly we can apply the custom action filter to action and controller. And there are a few things to keep in mind when we’re creating a custom action filter.
- IAuthorizeFilter
- IActionFilter
- IResultFilter
- IExceptionFilter
- FilterAttribute
- ActionFilterAttribute
- AuthorizeAttribute
And we need to keep in mind these four important methods as well.
- OnActionExecuting
- OnActionExecuted
- OnResultExecuting
- OnResultExecuted
Because each action filter must implement these interfaces, base classes, and methods, so make sure you know these things before developing custom action filter. It was a very simple example to make sense to show you how we work with custom action filters.