Accessing The Model From Filters In Razor Pages

Support for filters was added to Razor Pages with the release of ASP.NET Core 2.1. Filters, for those unfamiliar with them, are components that enable the injection of processing at certain points within the Razor Page lifecycle. They differ from Middleware in that they provide access to the HttpContext. Razor Pages offers a number of different filter options. One of the things that you might want to do in a filter is to obtain data from an external resource (database, web service etc) and then use it in the PageModel or ViewData. Here, I take a look at how you can access the model or ViewData dictionary depending on the type of filter implementation that you choose.

IPageFilter

The Razor Pages-specific filter implementation is represented by the IPageFilter interface and its asynchronous counterpart, IAsyncPageFilter.The examples here will use the asynchronous version because that is what you are most likely to use if you re getting data from elsewhere. The IAsyncPageFilter interface is implemented by the Razor Pages PageModel class and enables you to insert processing logic at the point that the page's handler method has been selected, but before model binding has happened (OnPageHandlerSelectionAsync method), and at the point before the handler is executed, but after model binding has taken place (OnPageHandlerExecutionAsync method) When you use either of these methods from within a class that derives from PageModel (e.g. any Razor Pages "code behind" file), you automatically have access to the current PageModel and its ViewData.

Filters are mostly used to execute code that runs for any number of pages. Therefore you are more likely to implement the IPageFilter or IAsyncPageFilter methods in a base PageModel class that your individual pages derive from to adhere to DRY principals rather then re-implement the same code in numerous PageModel files. Again, PageModel properties declared in this type of class are readily accessible.

Alternatively, if you have multiple filters, you might create separate filter classes that implement IAsyncPageFilter and register them globally. When you do that, the model is not so obviously accessible.

Global Filters

The following example demonstrates how to access the model from within a globally registered filter. It assumes that you have derived from PageModel to create a base PageModel class that all or most of your other Razor Pages will inherit. You might do this when you want to make the same data available to multiple pages. One example where this approach can be used is a permissions-based authorisation system. You need to be able to check if the current user has permission to e.g. view orders, create orders, edit orders etc, and then show or hide certain sections of the page (including the navigation) based on that. Filters are a good way of centralising the acquisition of the necessary data for those types of checks.

This particular Pagemodel class has one property for demo purposes: Found:

There are a couple of ways to access this property from within a globally registered filter class. They both begin with the context property that's passed in as an argument to the filter method that you implement. Here is an implementation of an IAsyncPageFilter illustrating both methods:

In the OnPageHandlerSelectionAsync method, the HandlerInstance property of the context parameter (representing a PageHandlerSelectedContext object) is accessed. The HandlerInstance property represents the object that the selected handler method resides within i.e. the current page model. The code uses GetType to establish whether the current page model derives from BasePageModel, and if so, it is cast to that type so that the Found property can be set.

The OnPageHandlerExecutionAsync method's context parameter (a PageHandlerExecutingContext) also exposes a HandlerInstance property so the previous approach can also be used in this method. However, this example demonstrates an alternative that is only available in this method. This time, the context's Result property is accessed. This represents the ActionResult that the handler method will generate. We are only interested in results that render a Razor Page i.e. a PageResult type, so the code filters out other results. The result is cast to the correct type, and the Model property can be accessed directly as can the ViewData dictionary if desired:

var viewData = ((PageResult)result).ViewData;

Once again, GetType is used to establish that the current model derives from BasePageModel and the Found property is accessed accordingly.

Filters as Attributes

If you want to implement your filter as an attribute, you have two options: derive from ActionFilterAttribute or from ResultFilterAttribute. In either case, you should override the OnResultExecutionAsync method because the OnActionExecutionAsync method is only used for MVC. And in both cases the ResultExecutingContext parameter passed into the methods exposes a Result property which can be used in the same way as the global filter implementation:

Summary

Filters in Razor Pages are very flexible and can be implemented in a number or ways. Regardless of your chosen implementation, it is relatively easy to access the PageModel once you know how.