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.