Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Razor Pages Filters #5835

Merged
merged 31 commits into from
Apr 12, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d425cd3
WIP Razor Pages Filters
Rick-Anderson Mar 30, 2018
0d9a6ea
spelen
Rick-Anderson Mar 30, 2018
2c00b5c
Merge branch 'master' of https://github.com/aspnet/Docs into rpFilter/ra
Rick-Anderson Mar 30, 2018
30cfc85
clean up
Rick-Anderson Mar 30, 2018
125da1a
clean up
Rick-Anderson Mar 30, 2018
08cdcd9
mvc filters UE edit
Rick-Anderson Mar 30, 2018
aad6a5b
Merge branch 'master' into rpFilter/ra
Rick-Anderson Apr 4, 2018
3606904
wrok
Rick-Anderson Apr 5, 2018
3af1e78
Merge branch 'rpFilter/ra' of https://github.com/aspnet/Docs into rpF…
Rick-Anderson Apr 5, 2018
3d30d68
wrok
Rick-Anderson Apr 5, 2018
fdf11c9
wrok
Rick-Anderson Apr 5, 2018
e78afda
react to feedback
Rick-Anderson Apr 5, 2018
25ee5f1
minor
Rick-Anderson Apr 5, 2018
0507973
minor
Rick-Anderson Apr 5, 2018
5a56ab0
minor
Rick-Anderson Apr 6, 2018
d7d8357
minor
Rick-Anderson Apr 6, 2018
7b4de6e
minor
Rick-Anderson Apr 6, 2018
47a920c
minor
Rick-Anderson Apr 6, 2018
17fb0f9
Merge branch 'master' into rpFilter/ra
Rick-Anderson Apr 10, 2018
9f51ae9
minor
Rick-Anderson Apr 10, 2018
8dd8caa
Merge branch 'rpFilter/ra' of https://github.com/aspnet/Docs into rpF…
Rick-Anderson Apr 10, 2018
74adc9a
minor
Rick-Anderson Apr 12, 2018
2a02b83
minor
Rick-Anderson Apr 12, 2018
0fde88b
minor
Rick-Anderson Apr 12, 2018
57763cf
minor
Rick-Anderson Apr 12, 2018
2706a1e
react to feedback
Rick-Anderson Apr 12, 2018
7d38035
fix
Rick-Anderson Apr 12, 2018
6465eff
fix
Rick-Anderson Apr 12, 2018
61b33b0
fix
Rick-Anderson Apr 12, 2018
310c85c
fix
Rick-Anderson Apr 12, 2018
8a2e1f2
fix
Rick-Anderson Apr 12, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 100 additions & 37 deletions aspnetcore/mvc/controllers/filters.md

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions aspnetcore/mvc/razor-pages/filter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
title: Filter methods for Razor Pages in ASP.NET Core
author: Rick-Anderson
description: Learn how to create filter methods for Razor Pages in ASP.NET Core.
manager: wpickett
ms.author: riande
ms.date: 4/5/2018
ms.prod: asp.net-core
ms.technology: aspnet
ms.topic: content
uid: mvc/razor-pages/filter
---
# Filter methods for Razor Pages in ASP.NET Core

By [Rick Anderson](https://twitter.com/RickAndMSFT)

Razor Page filters [IPageFilter](/dotnet/api/microsoft.aspnetcore.mvc.filters.ipagefilter?view=aspnetcore-2.0) and [IAsyncPageFilter](/dotnet/api/microsoft.aspnetcore.mvc.filters.iasyncpagefilter?view=aspnetcore-2.0) allow Razor Pages to run code before and after a Razor Page handler is run. Razor Page filters are similar to [ASP.NET Core MVC action filters](xref:mvc/controllers/filters#action-filters).

Razor Page filters:

* Run code after a handler method has been selected, but before model binding occurs.
* Run code before the handler method executes, after model binding is complete.
* Run code after the handler method executes.
* Can be implemented on a page or globally.

Code can be run before a handler method executes using the page constructor or middleware, but only Razor Page filters have access to [HttpContext](/dotnet/api/microsoft.aspnetcore.mvc.razorpages.pagemodel.httpcontext?view=aspnetcore-2.0#Microsoft_AspNetCore_Mvc_RazorPages_PageModel_HttpContext). Filters have a [FilterContext](/dotnet/api/microsoft.aspnetcore.mvc.filters.filtercontext?view=aspnetcore-2.0) derived parameter, which provides access to `HttpContext`.

[View or download sample code](https://github.com/aspnet/Docs/tree/live/aspnetcore/tutorials/razor-pages/razor-pages-start/sample/RazorPagesMovie) ([how to download](xref:tutorials/index#how-to-download-a-sample))

Razor Page filters provide the following methods, which can be applied globally or at the page level:

* Synchronous methods:

* [OnPageHandlerSelected](/dotnet/api/microsoft.aspnetcore.mvc.filters.ipagefilter.onpagehandlerselected?view=aspnetcore-2.0) : Called after a handler method has been selected, but before model binding occurs.
* [OnPageHandlerExecuting](/dotnet/api/microsoft.aspnetcore.mvc.filters.ipagefilter.onpagehandlerexecuting?view=aspnetcore-2.0) : Called before the handler method executes, after model binding is complete.
* [OnPageHandlerExecuted](/dotnet/api/microsoft.aspnetcore.mvc.filters.ipagefilter.onpagehandlerexecuted?view=aspnetcore-2.0) : Called after the handler method executes, before the action result.

* Asynchronous methods:

* [OnPageHandlerSelectionAsync](/dotnet/api/microsoft.aspnetcore.mvc.filters.iasyncpagefilter.onpagehandlerselectionasync?view=aspnetcore-2.0) : Called asynchronously after the handler method has been selected, but before model binding occurs.
* [OnPageHandlerExecutionAsync](/dotnet/api/microsoft.aspnetcore.mvc.filters.iasyncpagefilter.onpagehandlerexecutionasync?view=aspnetcore-2.0) : Called asynchronously before the handler method is invoked, after model binding is complete.

> [!NOTE]
> Implement **either** the synchronous or the async version of a filter interface, not both. The framework checks first to see if the filter implements the async interface, and if so, it calls that. If not, it calls the synchronous interface's method(s). If both interfaces are implemented, only the async methods are be called. The same rule applies to overrides in pages, implement the synchronous or the async version of the override, not both.

## Implement Razor Page filters globally

The following code implements an `IAsyncPageFilter`:

[!code-csharp[Main](filter/sample/PageFilter/Filters/SampleAsyncPageFilter.cs?name=snippet1)]

In the preceding code, [ILogger](/dotnet/api/microsoft.extensions.logging.ilogger?view=aspnetcore-2.0) is not required. It's used in the sample to provide trace information for the application.

The following code enables the `SampleAsyncPageFilter` in the `Startup` class:

[!code-csharp[Main](filter/sample/PageFilter/Startup.cs?name=snippet2&highlight=11)]

The following code shows the complete `Startup` class:

[!code-csharp[Main](filter/sample/PageFilter/Startup.cs?name=snippet1)]

The following code implements the synchronous `IPageFilter`:

[!code-csharp[Main](filter/sample/PageFilter/Filters/SamplePageFilter.cs?name=snippet1)]

The following code enables the `SamplePageFilter`:

[!code-csharp[Main](filter/sample/PageFilter/StartupSync.cs?name=snippet2&highlight=11)]

## Implement Razor Page filters by overriding page handlers
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overriding filter methods (not handlers)


The following code overrides the synchronous Razor Page filters:

[!code-csharp[Main](filter/sample/PageFilter/Pages/Index.cshtml.cs)]

The following code overrides the asynchronous Razor Page filters:

[!code-csharp[Main](filter/sample/PageFilter/Pages/Contact.cshtml.cs)]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This class is used to create the logger type in Startup.
namespace PageFilter.Filters
{
public class GlobalFiltersLogger
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#region snippet1
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace PageFilter.Filters
{
public class SampleAsyncPageFilter : IAsyncPageFilter
{
private readonly ILogger _logger;

public SampleAsyncPageFilter(ILogger logger)
{
_logger = logger;
}

public async Task OnPageHandlerSelectionAsync(
PageHandlerSelectedContext context)
{
_logger.LogDebug("Global OnPageHandlerSelectionAsync called.");
await Task.CompletedTask;
}

public async Task OnPageHandlerExecutionAsync(
PageHandlerExecutingContext context,
PageHandlerExecutionDelegate next)
{
_logger.LogDebug("Global OnPageHandlerExecutionAsync called.");
await next.Invoke();
}
}
}
#endregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#region snippet1
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;

namespace PageFilter.Filters
{
public class SamplePageFilter : IPageFilter
{
private readonly ILogger _logger;

public SamplePageFilter(ILogger logger)
{
_logger = logger;
}

public void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
_logger.LogDebug("Global sync OnPageHandlerSelected called.");
}

public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
{
_logger.LogDebug("Global sync PageHandlerExecutingContext called.");
}

public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
{
_logger.LogDebug("Global sync OnPageHandlerExecuted called.");
}
}
}
#endregion
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>


<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0-preview2-30457" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just point this to 2.1.0-preview2-final (when it's available, which is likely soon)?

</ItemGroup>


</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@page
@model AboutModel
@{
ViewData["Title"] = "About";
}
<h2>@ViewData["Title"]</h2>
<h3>@Model.Message</h3>

<p>Use this area to provide additional information.</p>

<div class="col-md-12">
<form method="post">
<input type="submit" id="send" value="Post" />
</form>
<p></p>
<ul id="messages" style="list-style-type:none;"></ul>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace PageFilter.Pages
{
public class AboutModel : PageModel
{
private readonly ILogger _logger;

public AboutModel(ILogger<AboutModel> logger)
{
_logger = logger;
}

public string Message { get; set; }

public void OnGet()
{
Message = "Your application description page.";
_logger.LogDebug("About/OnGet");
}

public void OnPost()
{
_logger.LogDebug("About/OnPost");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@page
@model ContactModel
@{
ViewData["Title"] = "Contact";
}
<h2>@ViewData["Title"]</h2>
<h3>@Model.Message</h3>

<address>
One Microsoft Way<br />
Redmond, WA 98052-6399<br />
<abbr title="Phone">P:</abbr>
425.555.0100
</address>

<address>
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace PageFilter.Pages
{
public class ContactModel : PageModel
{
private readonly ILogger _logger;

public ContactModel(ILogger<ContactModel> logger)
{
_logger = logger;
}
public string Message { get; set; }

public async Task OnGetAsync()
{
Message = "Your contact page.";
_logger.LogDebug("Contact/OnGet");
await Task.CompletedTask;
}

public override async Task OnPageHandlerSelectionAsync(
PageHandlerSelectedContext context)
{
_logger.LogDebug("ContactModel/OnPageHandlerSelectionAsync called.");
await Task.CompletedTask;
}

public override async Task OnPageHandlerExecutionAsync(
PageHandlerExecutingContext context,
PageHandlerExecutionDelegate next)
{
_logger.LogDebug("ContactModel/OnPageHandlerExecutionAsync called.");
await next.Invoke();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}

<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>

@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}

<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace PageFilter.Pages
{
public class ErrorModel : PageModel
{
public string RequestId { get; set; }

public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@page "{id:int?}"
@model IndexModel
@{
ViewData["Title"] = "Home page";
}

<h2>@ViewData["Title"]</h2>
<h3>@Model.Message</h3>

<p>Simplified Index page.</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;

namespace PageFilter.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger _logger;

public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public string Message { get; set; }

public void OnGet()
{
_logger.LogDebug("IndexModel/OnGet");
}

public override void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
_logger.LogDebug("IndexModel/OnPageHandlerSelected");
}

public override void OnPageHandlerExecuting(PageHandlerExecutingContext context)
{
Message = "Message set in handler executing";
_logger.LogDebug("IndexModel/OnPageHandlerExecuting");
}


public override void OnPageHandlerExecuted(PageHandlerExecutedContext context)
{
_logger.LogDebug("IndexModel/OnPageHandlerExecuted");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h2>@ViewData["Title"]</h2>

<p>Use this page to detail your site's privacy policy.</p>
Loading