Exercise 1: Logging Actions

In this exercise, you will learn how to create a custom action log filter by using MVC3 Filter Providers. For that purpose you will apply a logging filter to the MusicStore site that will record all the activities in the selected controllers.

The filter will extend ActionFilterAttributeClass and override OnActionExecuting method to catch each request and then perform the logging actions. The context information about HTTP requests, executing methods, results and parameters will be provided by MVC ActionExecutingContext class.

About MVC Music Store Application logging feature

This Music Store solution has a new data model table for site logging, ActionLog, with the following fields: Name of the controller that received a request, Called action, Client IP and Time stamp.

Figure 1

Data model. ActionLog table.

The solution provides an MVC View for the Action log that can be found at MvcMusicStores/Views/ActionLog:

Figure 2

Action Log view

With this given structure, all the work will be focused on interrupting controller’s request and performing the logging by using custom filtering.

Task 1 – Creating a Custom Filter to Catch a Controller’s Request

In this task you will create a custom filter attribute class that will contain the logging logic. For that purpose you will extend MVC ActionFilterAttribute Class and implement the interface IActionFilter.

Note:
ActionFilterAttribute is the base class for all the attribute filters. It provides the following methods to execute a specific logic after and before controller action’s execution:

- OnActionExecuting(ActionExecutedContext filterContext)

Just before the action method is called

- OnActionExecuted(ActionExecutingContext filterContext):

After the action method is called and before the result is executed (before view render).

- OnResultExecuting(ResultExecutingContext filterContext):

Just before the result is executed (before view render).

- OnResultExecuted(ResultExecutedContext filterContext):

After the result is executed (after the view is rendered).

By overriding any of these methods into a derived class, you can execute your own filtering code.

  1. Open the begin solution MvcMusicStore.sln at Source\Ex01-Logging Actions\Begin
  2. Create a new folder Filters at project root, which will include all the custom filters.
  3. Add a new C# or Visual Basic class into the Filters folder and rename it to ActionLogFilterAttribute.[cs|vb]
  4. Open ActionLogFilterAttribute.[cs|vb] and add a reference to System.Web.Mvc and the MvcMusicStore.Models namespace:

    C#

    using System;
    FakePre-897483aa8d164b60a9f7e47690aac938-7c3ba04dd2764f639e08055aa8f2761eFakePre-a4d9aaecd53546a6b4694d5f3d084eba-4b26814012d4494dbd2eb2edab0abb39FakePre-fa13b93799e149669517b6b6d0afd7f3-16e6efba671e48bd9d3b2056c8998033using System.Web.Mvc; using MvcMusicStore.Models;

    Visual Basic

    Imports System
    FakePre-590a5e98826a4551903d8e70dfa2e87f-431a1f6ae54d420ab784233a8b9237d4FakePre-d22fdd4ca43f463e91b78a8d529f1762-bd8c2cc316264bc3bdc79d144219d15bFakePre-eb56b265e13a4125bfa2e8d64c271c75-752832715c064b0b9d462cd42cc431b5FakePre-a4d7642d15d546858c3a8ce452fd5937-5f7cf11dbd1c4185b143f280d8e93f55FakePre-6562663dab4f4f8f8f5af7aa007870ce-03694fb201004acea2eb7c6c705868d9

  5. Inherit the ActionLogFilterAttribute class from ActionFilterAttribute and then make ActionLogFilterAttribute class implement IActionFilter interface.

    C#

    ...
    FakePre-256b416500e341d9bcedcfdfff0bbfdb-f60e60c50a20495ab38f764664282c37FakePre-68cd282076fd4d2b814fcabf444b2a4b-d657d9619dc04d929583288ae8ed4930FakePre-7ffa4b195035410ca47ace1216c44e9f-c36c9cab84dd4513b7784239cd2bcf4fFakePre-60e43e037ede40b198afeeceae346956-a5ce237adeb74631a191737065862c49FakePre-d1aac538110445949d876495b24012cf-e8c1d20a6f954d648c9af34c5d89f419

    Visual Basic

    ...
    FakePre-0b2d1921307c4915a211bd903341d1dd-2f00dd45bae44925b119702e10907d34FakePre-40dee3ad63d948579331744df80abd1a-814cecc423a048b4b10fdea7810271ecFakePre-df2ad66bf2b24f0698aec10a8aadafb5-21f1e6145987474a93e23dc495cbbf16FakePre-2540b8c82807433fbf1348a0563092d8-7c8d87442a224443add409ca5043d2b0FakePre-6f4c8a6447734ddab3765652d97327ba-0b24e413718a49658b80e398b9485ff9

  6. Make ActionLogFilterAttribute class override the method OnActionExecuting, where you will write the logging code. After that, your class should look like the following:

    (Code Snippet – ASP.NET MVC 3 Custom Action Filters – Ex1 Logging Actions – CSharp)

    C#

    using System;
    FakePre-e0118f2e910b40fc91d50134ab646805-b8b17af694974b30aa45d225956358e5FakePre-b7bc36ed0ae54474be03943bac1db244-669c6fab63d84a87bfb7ca4093d539ffFakePre-8aa3afd994744595a48d658627a4d8f0-d21a50e902794f4584421e18a8a417cdFakePre-c8e9d578f6fd43adb39488995cf165d7-759d9c14174b41e88ec80661aab77a9fFakePre-f159f4a658fb482799ffff3b61a05b47-bd8f2d916f32444ca2e897b8641af1c8FakePre-45211b968b974fd5a69bf5317cc6855e-556b497cccc343bb8d3fb7df0f7c6cabFakePre-1a7ae69744c345b4834c28ed0540eead-4e84c49df609479cb2541f93b57b62e3FakePre-3dda27f43c8b464ca7b76f0c10f78bd8-4547e61aade1485680bded57c6f40cbcFakePre-7934025800f44d34af3197e23a22ef34-dc1bccbadbd548e4b74ad2fc6dafb794FakePre-c93f01ce919b43438b7668c58be738c1-d789c68090c142789912dda98bcd8a23 public override void OnActionExecuting(ActionExecutingContext filterContext) { MusicStoreEntities storeDB = new MusicStoreEntities(); ActionLog log = new ActionLog() { Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, Action = filterContext.ActionDescriptor.ActionName, IP = filterContext.HttpContext.Request.UserHostAddress, DateTime = filterContext.HttpContext.Timestamp }; storeDB.AddToActionLogs(log); storeDB.SaveChanges(); base.OnActionExecuting(filterContext); }FakePre-9f6563f0d7e74dd5a8dc3eb4ffc39f3b-35b9980f50684e89a80a04ff7ac06b08FakePre-923295469e1b4322abac99052c645db4-69156edce06646e3a30154f19f39b32eFakePre-5cf2b537792549cd82b294dccfcfe4dc-72fcb554a2ff4a3aa32d6eb613ec0e8dFakePre-8458872ef522403abd1a26981045d061-b8ba8ecbdf3d4bd88f9ed76ccdba0c7cFakePre-0eeb6a40c42045fd819c5aef553a8935-679a1892fcb54725aeba0929334b2337

    (Code Snippet – ASP.NET MVC 3 Custom Action Filters – Ex1 Logging Actions – VB)

    Visual Basic

    Imports System
    FakePre-a1d0c3fa23244b4b86bcd4c16983d186-24d2be6de7494ab7a92adb7686017a3fFakePre-9456bec101b94942af466d629c33bedd-b5ce29de514e4338a94d771a48dae0ceFakePre-807641dd29544b96b57f5c22c82d72a9-58deec6d9a1b40abba9166869e546a79FakePre-6d2a17b423a84a27b76f21e06a51b356-33392ff1df89413984d1cb1a3a720e20FakePre-759a364203074993a2f6f1f007287f6f-a843b59c9e464674b14719869e804331FakePre-27f66a15cf464266b4d2bb9a61a83e97-171cc30456a24c68b5a9473c9a3675f8FakePre-62a5e6d6b0e1432dad8a38cd2d4a382b-e182a6e376db4401899a669e889d93d5FakePre-25201e840d38492f9714e57184b2e7f7-eace68071dd446a397b5b2b247afeb68FakePre-875481bb10d6472799f56c3d4fb43d93-cdbd6087716a4fa987659833ef08be7eFakePre-5358a0ed6f5e4a4692e9285200a09f7e-ab5152278d5545d4932c32354fcd0c3d Public Overrides Sub OnActionExecuting(ByVal filterContext As ActionExecutingContext) Dim storeDB As New MusicStoreEntities Dim log As New ActionLog With { .Controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName, .Action = filterContext.ActionDescriptor.ActionName, .IP = filterContext.HttpContext.Request.UserHostAddress, .DateTime = filterContext.HttpContext.Timestamp} storeDB.AddToActionLogs(log) storeDB.SaveChanges() MyBase.OnActionExecuting(filterContext) End SubFakePre-8e923ab2497e49aba609ca595298ba83-41896923529641bda9e43ee87e3921deFakePre-0899f386b6c84160818156c765f88f75-78abd620c6d841d8ba72c6831ad2fadeFakePre-f2b4f37526414468ac008d35ffd3cc85-f4a52f59cc7d49e4a7e49bb45b60fad1FakePre-e44a94fa6e444b7fb87efd54f96a7c5f-eeec4fe3b8524fc1a267a49e323283f4FakePre-52cdc598705149919a7ef39150461408-556b8ee376bf4c1d8e271ced25715fd5

    Note:
    Note: OnActionExecuting method is using Entity Framework to add a new ActionLog register. It creates and fills a new entity instance with the context information from filterContext.

    You could read more about ControllerContext class at msdn.

Task 2 – Injecting a Code Interceptor into the Store Controller Class

In this task you will add the custom filter by injecting it to all controller classes and controller actions that will be logged. For the purpose of this exercise, the Store Controller class will have a log.

The method OnActionExecuting from ActionLogFilterAttribute custom filter runs when an injected element is called.

It is also possible to intercept a specific controller method.

  1. Open the StoreController at MvcMusicStore\Controllers and add a reference to the Filters namespace:

    C#

    FakePre-a18f7331d0054f31935595f2593efa4d-2d3c6364ce57441d88efc59e4f6820c0FakePre-6f93b71ecdea4a348ddc5cfcaecb8c12-55a51e9ceebd4b9b8ce8ae553da8cbabusing MvcMusicStore.Filters;FakePre-1c1d3c23aa6e4c48bf74c2a0bfdb6769-5e98e68de0cf4b1ea132b2292c1d845dFakePre-b11c3126a23d4184a35c86ffe315bffd-0d703efd023846fcbe86057988fb7c51FakePre-d4db8004391a43c4a0464d707e4d5957-ea2f0abb9b67412f99058268146055cfFakePre-00fe461501b34f4b9f325edab3d73cec-2d8e7ecd156d49998096b3096231c1b8

    Visual Basic

    FakePre-b274dad32a334507b6075f69f281d9ff-c126850a64d8489fa52cefc03e1102f7FakePre-c08be0eb8b5544fa8e9b18c4265488bf-f21869d704324c068f70960187b77f6fFakePre-98ce61a591394ebaa9b19082634669c1-a4dd8f0a4d1a4ab69f958d7d7523ef7fFakePre-b225f87d136541c0ae649dcb0abd1fdd-d2a46cec9ea14d5dba9dd4d8e0eaacc4FakePre-c3d6bf858b9a4acd84bacd9a317d2192-ea169cfff9084f82873c2ef6bb4b87c0FakePre-3ea5b778e6a647ee8056fb18f9e39830-ddb91acb392c47fd830aa883da0a3f15FakePre-fcc15eacb21e4a51a4eebc476e0811e2-4a2575b86db049109eea478cbb921b48
  2. Inject the custom filter ActionLogFilter into StoreController class.

    C#

    FakePre-9d5293eea3fb4ac4a4ac4bd57920465c-14fcee441a874f62bf45648f4fc93dd3FakePre-a206194741eb4d7486c65d3a65d9f89f-94d9474d00fa4e5cbca1c2577671e523FakePre-d1866f33cf454604b88801c7616903ae-15ef70abd9ff4bc2bc09d03657306975FakePre-bc0a335e786f4e3b8907d9cfa302f82e-8a71ea7ff5c442818691d61cbb46d2fcFakePre-7c03deb195164643899cf9b63a2bd785-c38f38cd5cf1485ebc43623636f24e18FakePre-b23e9c74eba54760ad340d9de32de846-5671a361811643ac8e8a0172eabb7b58 [ActionLogFilter]FakePre-e550c49bf03f4b28b143611e1822e05f-5ef62e86083e455ca7c1e39baa3a5edcFakePre-e6adbd22bf274d828ee619ca5f42d66e-c5ce0d48b0274c928b98d1ade54bfc3eFakePre-2929b4a55d6148208743b4dba36f626e-ce347e736b14472c85341c41c834fecdFakePre-fb597e76412c480b83fadcb6f0e4e1ae-5778b3625b0f4722b89bd5ca173e2eafFakePre-35debb6c260e482999ced258fae21518-a54ca7b270514f29abbbe2dfdeafe1b6FakePre-e4a2d8db83f943508958c5f4e4503ed3-9f93d374a7e841dcafdee5211678e428FakePre-3fa5b67d6d344f74a0ec5b6f24dcfe35-bc8cb0ff1510467aa4edca6221090ddeFakePre-e506e7fd8b69440795a95ff3f2153ea2-f5533f67380f4df4ac47dc299bcd1c19FakePre-049c4d0e2de24e35bb65dc042c0e0950-e874f420f51f424faff9b653b4491481FakePre-a1b6938d5ee049838e2a5d15bc8264b2-05399b6668794c89a0633aa0d56cb5f3

    Visual Basic

    FakePre-2ba8bdd77a7c4337887acc26881dfae5-e14e869b69c543ecb5ec711f7565264aFakePre-24e7baaabdda4ee6be513effbd65ba4f-a1e28e68827442abaf8a9027e14d5bddFakePre-25a68806941240d488b45f5ac55f1f80-2e7e25071e9940b99d870cff561b5c3eFakePre-c776f3aba8f24c838359aa7adb33db9e-0f8771a7a7074173be22b047c8ea22bdFakePre-7caad94bbc144823831c80696c83e1b4-f155fc1ff7744957879382ec85d0f808FakePre-74015cd84ad44a40b4f984c0d20a4f31-c6b72f50815d413db64cb48f8c2a1269 <ActionLogFilter>FakePre-1a6c8bdbf6fe4cd8acc6a9356e191746-7e34ff8d34d145288b229bf6eeb5f630FakePre-eedee4c7094744d8b296e92525d31ba6-b3aedc1761e24ed68e3713d1fbc51677FakePre-b76ef13d5e3f4f52abf788955682ddbd-ddacfd0e43fd41419cc2d94b68a41552FakePre-fd73bcf5bb0a48c796328c7436917eaa-886ed95be84449f0bed6e5079ef5fda1FakePre-1b4187cac52d49dcb0b216ddd9140161-cb2e122b0a744fc7b94e4d8c513196e0FakePre-f8d7835ac900495e82527435fbcb0868-c5250b0199504aae9cceca8650071364FakePre-b2bcf2d297aa46f1b92f1ba20ab9ec3d-e2454411140145b4b2a36d9c824d4fdbFakePre-0ba77bf9ab014ccea58b318d3482c189-b2384e39b3314e6199c99971bc6dca96FakePre-35eef1b2fa61473e8db6ab4c9ff02045-64a952efdb314797a87b9a56cb3a70b2FakePre-9f78fe02fbc4497abf9caa96080d3e2d-f298faa8a69c405eba632a1e43137da9

    Note:
    When a filter is injected into a controller class, all its actions are also injected. If you would like to apply the filter only for a set of actions, you would have to inject [ActionLogFilter] to each one of them:

    [ActionLogFilter]

    public ActionResult Index()

    {

    }

    [ActionLogFilter]

    public ActionResult Browse(string genre)

    {

    }

    Visual Basic

    <ActionLogFilter>

    Public Function Index() As ActionResult

    End Function

    <ActionLogFilter>

    Public Function Browse(ByVal genre As String) As ActionResult

Task 3 – Running the Application

In this task, you will test that the logging filter is working. You will start the application and visit the store, and then you will check logged activities.

  1. Press F5 to run the application.
  2. Browse to /ActionLog to see log view initial state:

    Figure 3

    Log tracker status before page activity

  3. Browse to /Store and perform some actions there, like browsing an album detail.
  4. Browse to /ActionLog and if the log is empty press F5 to refresh the page. Check that your visits were tracked:

    Figure 4

    Action log with activity logged

Next Step

Summary