diff --git a/aspnetcore/mvc/controllers/filters.md b/aspnetcore/mvc/controllers/filters.md
index 0902a4112fac..1764b15458b6 100644
--- a/aspnetcore/mvc/controllers/filters.md
+++ b/aspnetcore/mvc/controllers/filters.md
@@ -3,8 +3,8 @@ title: Filters in ASP.NET Core
author: ardalis
description: Learn how filters work and how to use them in ASP.NET Core MVC.
manager: wpickett
-ms.author: tdykstra
-ms.date: 12/12/2016
+ms.author: riande
+ms.date: 4/10/2018
ms.prod: asp.net-core
ms.technology: aspnet
ms.topic: article
@@ -13,16 +13,20 @@ uid: mvc/controllers/filters
# Filters in ASP.NET Core
-By [Tom Dykstra](https://github.com/tdykstra/) and [Steve Smith](https://ardalis.com/)
+By [Rick Anderson](https://twitter.com/RickAndMSFT), [Tom Dykstra](https://github.com/tdykstra/), and [Steve Smith](https://ardalis.com/)
-*Filters* in ASP.NET Core MVC allow you to run code before or after certain stages in the request processing pipeline.
+*Filters* in ASP.NET Core MVC allow you to run code before or after specific stages in the request processing pipeline.
> [!IMPORTANT]
-> This topic does **not** apply to Razor Pages. ASP.NET Core 2.1 preview and later supports `IPageFilter` and `IAsyncPageFilter` for Razor Pages.
+> This topic does **not** apply to Razor Pages. ASP.NET Core 2.1 preview and later supports [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) for Razor Pages. For more information, see [Filter methods for Razor Pages](xref:mvc/razor-pages/filter).
- Built-in filters handle tasks such as authorization (preventing access to resources a user isn't authorized for), ensuring that all requests use HTTPS, and response caching (short-circuiting the request pipeline to return a cached response).
+ Built-in filters handle tasks such as:
+
+ * Authorization (preventing access to resources a user isn't authorized for).
+ * Ensuring that all requests use HTTPS.
+ * Response caching (short-circuiting the request pipeline to return a cached response).
-You can create custom filters to handle cross-cutting concerns for your application. Anytime you want to avoid duplicating code across actions, filters are the solution. For example, you can consolidate error handling code in a exception filter.
+Custom filters can be created to handle cross-cutting concerns. Filters can avoid duplicating code across actions. For example, an error handling exception filter could consolidate error handling.
[View or download sample from GitHub](https://github.com/aspnet/Docs/tree/master/aspnetcore/mvc/controllers/filters/sample).
@@ -38,13 +42,13 @@ Each filter type is executed at a different stage in the filter pipeline.
* [Authorization filters](#authorization-filters) run first and are used to determine whether the current user is authorized for the current request. They can short-circuit the pipeline if a request is unauthorized.
-* [Resource filters](#resource-filters) are the first to handle a request after authorization. They can run code before the rest of the filter pipeline, and after the rest of the pipeline has completed. They're useful to implement caching or otherwise short-circuit the filter pipeline for performance reasons. Since they run before model binding, they're useful for anything that needs to influence model binding.
+* [Resource filters](#resource-filters) are the first to handle a request after authorization. They can run code before the rest of the filter pipeline, and after the rest of the pipeline has completed. They're useful to implement caching or otherwise short-circuit the filter pipeline for performance reasons. They run before model binding, so they can influence model binding.
* [Action filters](#action-filters) can run code immediately before and after an individual action method is called. They can be used to manipulate the arguments passed into an action and the result returned from the action.
* [Exception filters](#exception-filters) are used to apply global policies to unhandled exceptions that occur before anything has been written to the response body.
-* [Result filters](#result-filters) can run code immediately before and after the execution of individual action results. They run only when the action method has executed successfully and are useful for logic that must surround view or formatter execution.
+* [Result filters](#result-filters) can run code immediately before and after the execution of individual action results. They run only when the action method has executed successfully. They are useful for logic that must surround view or formatter execution.
The following diagram shows how these filter types interact in the filter pipeline.
@@ -52,24 +56,24 @@ The following diagram shows how these filter types interact in the filter pipeli
## Implementation
-Filters support both synchronous and asynchronous implementations through different interface definitions. Choose either the sync or async variant depending on the kind of task you need to perform.
+Filters support both synchronous and asynchronous implementations through different interface definitions.
Synchronous filters that can run code both before and after their pipeline stage define On*Stage*Executing and On*Stage*Executed methods. For example, `OnActionExecuting` is called before the action method is called, and `OnActionExecuted` is called after the action method returns.
-[!code-csharp[](./filters/sample/src/FiltersSample/Filters/SampleActionFilter.cs?&name=snippet1)]
+[!code-csharp[](./filters/sample/src/FiltersSample/Filters/SampleActionFilter.cs?name=snippet1)]
Asynchronous filters define a single On*Stage*ExecutionAsync method. This method takes a *FilterType*ExecutionDelegate delegate which executes the filter's pipeline stage. For example, `ActionExecutionDelegate` calls the action method, and you can execute code before and after you call it.
[!code-csharp[](./filters/sample/src/FiltersSample/Filters/SampleAsyncActionFilter.cs?highlight=6,8-10,13)]
-You can implement interfaces for multiple filter stages in a single class. For example, the [ActionFilterAttribute](https://docs.microsoft.com/aspnet/core/api/microsoft.aspnetcore.mvc.filters.actionfilterattribute) abstract class implements both `IActionFilter` and `IResultFilter`, as well as their async equivalents.
+You can implement interfaces for multiple filter stages in a single class. For example, the [ActionFilterAttribute](/dotnet/api/microsoft.aspnetcore.mvc.filters.actionfilterattribute?view=aspnetcore-2.0) class implements `IActionFilter`, `IResultFilter`, and their async equivalents.
> [!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 you were to implement both interfaces on one class, only the async method would be called. When using abstract classes like [ActionFilterAttribute](https://docs.microsoft.com/aspnet/core/api/microsoft.aspnetcore.mvc.filters.actionfilterattribute) you would override only the synchronous methods or the async method for each filter type.
+> 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 you were to implement both interfaces on one class, only the async method would be called. When using abstract classes like [ActionFilterAttribute](/dotnet/api/microsoft.aspnetcore.mvc.filters.actionfilterattribute?view=aspnetcore-2.0) you would override only the synchronous methods or the async method for each filter type.
### IFilterFactory
-`IFilterFactory` implements `IFilter`. Therefore, an `IFilterFactory` instance can be used as an `IFilter` instance anywhere in the filter pipeline. When the framework prepares to invoke the filter, it attempts to cast it to an `IFilterFactory`. If that cast succeeds, the `CreateInstance` method is called to create the `IFilter` instance that will be invoked. This provides a very flexible design, since the precise filter pipeline doesn't need to be set explicitly when the application starts.
+`IFilterFactory` implements `IFilter`. Therefore, an `IFilterFactory` instance can be used as an `IFilter` instance anywhere in the filter pipeline. When the framework prepares to invoke the filter, it attempts to cast it to an `IFilterFactory`. If that cast succeeds, the `CreateInstance` method is called to create the `IFilter` instance that will be invoked. This provides a flexible design, since the precise filter pipeline doesn't need to be set explicitly when the app starts.
You can implement `IFilterFactory` on your own attribute implementations as another approach to creating filters:
@@ -106,7 +110,7 @@ Filter attributes:
## Filter scopes and order of execution
-A filter can be added to the pipeline at one of three *scopes*. You can add a filter to a particular action method or to a controller class by using an attribute. Or you can register a filter globally (for all controllers and actions) by adding it to the `MvcOptions.Filters` collection in the `ConfigureServices` method in the `Startup` class:
+A filter can be added to the pipeline at one of three *scopes*. You can add a filter to a particular action method or to a controller class by using an attribute. Or you can register a filter globally for all controllers and actions. Filters are added globally by adding it to the `MvcOptions.Filters` collection in `ConfigureServices`:
[!code-csharp[](./filters/sample/src/FiltersSample/Startup.cs?name=snippet_ConfigureServices&highlight=5-8)]
@@ -134,7 +138,12 @@ Here's an example that illustrates the order in which filter methods are called
| 5 | Controller | `OnActionExecuted` |
| 6 | Global | `OnActionExecuted` |
-This sequence shows that the method filter is nested within the controller filter, and the controller filter is nested within the global filter. To put it another way, if you're inside an async filter's On*Stage*ExecutionAsync method, all of the filters with a tighter scope run while your code is on the stack.
+This sequence shows:
+
+* The method filter is nested within the controller filter.
+* The controller filter is nested within the global filter.
+
+To put it another way, if you're inside an async filter's On*Stage*ExecutionAsync method, all of the filters with a tighter scope run while your code is on the stack.
> [!NOTE]
> Every controller that inherits from the `Controller` base class includes `OnActionExecuting` and `OnActionExecuted` methods. These methods wrap the filters that run for a given action: `OnActionExecuting` is called before any of the filters, and `OnActionExecuted` is called after all of the filters.
@@ -159,7 +168,7 @@ If you have the same 3 Action filters shown in the preceding example but set the
| 5 | Controller | 1 | `OnActionExecuted` |
| 6 | Method | 0 | `OnActionExecuted` |
-The `Order` property trumps scope when determining the order in which filters will run. Filters are sorted first by order, then scope is used to break ties. All of the built-in filters implement `IOrderedFilter` and set the default `Order` value to 0, so scope determines order unless you set `Order` to a non-zero value.
+The `Order` property trumps scope when determining the order in which filters will run. Filters are sorted first by order, then scope is used to break ties. All of the built-in filters implement `IOrderedFilter` and set the default `Order` value to 0. Fir built-in filters, scope determines order unless you set `Order` to a non-zero value.
## Cancellation and short circuiting
@@ -169,7 +178,12 @@ You can short-circuit the filter pipeline at any point by setting the `Result` p
[!code-csharp[](./filters/sample/src/FiltersSample/Filters/ShortCircuitingResourceFilterAttribute.cs?highlight=12,13,14,15)]
-In the following code, both the `ShortCircuitingResourceFilter` and the `AddHeader` filter target the `SomeResource` action method. However, because the `ShortCircuitingResourceFilter` runs first (because it's a Resource Filter and `AddHeader` is an Action Filter) and short-circuits the rest of the pipeline, the `AddHeader` filter never runs for the `SomeResource` action. This behavior would be the same if both filters were applied at the action method level, provided the `ShortCircuitingResourceFilter` ran first (because of its filter type, or explicit use of `Order` property, for instance).
+In the following code, both the `ShortCircuitingResourceFilter` and the `AddHeader` filter target the `SomeResource` action method. The `ShortCircuitingResourceFilter`:
+
+* Runs first, because it's a Resource Filter and `AddHeader` is an Action Filter.
+* Short-circuits the rest of the pipeline.
+
+Therefore the `AddHeader` filter never runs for the `SomeResource` action. This behavior would be the same if both filters were applied at the action method level, provided the `ShortCircuitingResourceFilter` ran first. The `ShortCircuitingResourceFilter` runs first because of its filter type, or by explicit use of `Order` property.
[!code-csharp[](./filters/sample/src/FiltersSample/Controllers/SampleController.cs?name=snippet_AddHeader&highlight=1,9)]
@@ -203,17 +217,27 @@ System.InvalidOperationException: No service for type
'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.
```
-`ServiceFilterAttribute` implements `IFilterFactory`, which exposes a single method for creating an `IFilter` instance. In the case of `ServiceFilterAttribute`, the `IFilterFactory` interface's `CreateInstance` method is implemented to load the specified type from the services container (DI).
+`ServiceFilterAttribute` implements `IFilterFactory`. `IFilterFactory` exposes the `CreateInstance` method for creating an `IFilter` instance. The `CreateInstance` method loads the specified type from the services container (DI).
### TypeFilterAttribute
-`TypeFilterAttribute` is very similar to `ServiceFilterAttribute` (and also implements `IFilterFactory`), but its type isn't resolved directly from the DI container. Instead, it instantiates the type by using `Microsoft.Extensions.DependencyInjection.ObjectFactory`.
+`TypeFilterAttribute` is similar to `ServiceFilterAttribute`, but its type isn't resolved directly from the DI container. It instantiates the type by using `Microsoft.Extensions.DependencyInjection.ObjectFactory`.
+
+Because of this difference:
-Because of this difference, types that are referenced using the `TypeFilterAttribute` don't need to be registered with the container first (but they will still have their dependencies fulfilled by the container). Also, `TypeFilterAttribute` can optionally accept constructor arguments for the type in question. The following example demonstrates how to pass arguments to a type using `TypeFilterAttribute`:
+* Types that are referenced using the `TypeFilterAttribute` don't need to be registered with the container first. They do have their dependencies fulfilled by the container.
+* `TypeFilterAttribute` can optionally accept constructor arguments for the type.
+
+The following example demonstrates how to pass arguments to a type using `TypeFilterAttribute`:
[!code-csharp[](../../mvc/controllers/filters/sample/src/FiltersSample/Controllers/HomeController.cs?name=snippet_TypeFilter&highlight=1,2)]
-If you have a filter that doesn't require any arguments, but which has constructor dependencies that need to be filled by DI, you can use your own named attribute on classes and methods instead of `[TypeFilter(typeof(FilterType))]`). The following filter shows how this can be implemented:
+If you have a filter that:
+
+* Doesn't require any arguments.
+* Has constructor dependencies that need to be filled by DI.
+
+You can use your own named attribute on classes and methods instead of `[TypeFilter(typeof(FilterType))]`). The following filter shows how this can be implemented:
[!code-csharp[](./filters/sample/src/FiltersSample/Filters/SampleActionFilterAttribute.cs?name=snippet_TypeFilterAttribute&highlight=1,3,7)]
@@ -221,21 +245,36 @@ This filter can be applied to classes or methods using the `[SampleActionFilter]
## Authorization filters
-*Authorization filters* control access to action methods and are the first filters to be executed within the filter pipeline. They have only a before method, unlike most filters that support before and after methods. You should only write a custom authorization filter if you are writing your own authorization framework. Prefer configuring your authorization policies or writing a custom authorization policy over writing a custom filter. The built-in filter implementation is just responsible for calling the authorization system.
+*Authorization filters:
+* Control access to action methods.
+* Are the first filters to be executed within the filter pipeline.
+* Have a before method, but no after method.
+
+You should only write a custom authorization filter if you are writing your own authorization framework. Prefer configuring your authorization policies or writing a custom authorization policy over writing a custom filter. The built-in filter implementation is just responsible for calling the authorization system.
-Note that you shouldn't throw exceptions within authorization filters, since nothing will handle the exception (exception filters won't handle them). Instead, issue a challenge or find another way.
+You shouldn't throw exceptions within authorization filters, since nothing will handle the exception (exception filters won't handle them). Consider issuing a challenge when an exception occurs.
Learn more about [Authorization](../../security/authorization/index.md).
## Resource filters
-*Resource filters* implement either the `IResourceFilter` or `IAsyncResourceFilter` interface, and their execution wraps most of the filter pipeline. (Only [Authorization filters](#authorization-filters) run before them.) Resource filters are especially useful if you need to short-circuit most of the work a request is doing. For example, a caching filter can avoid the rest of the pipeline if the response is already in the cache.
+* Implement either the `IResourceFilter` or `IAsyncResourceFilter` interface,
+* Their execution wraps most of the filter pipeline.
+* Only [Authorization filters](#authorization-filters) run before Resource filters.
-The [short circuiting resource filter](#short-circuiting-resource-filter) shown earlier is one example of a resource filter. Another example is [DisableFormValueModelBindingAttribute](https://github.com/aspnet/Entropy/blob/rel/1.1.1/samples/Mvc.FileUpload/Filters/DisableFormValueModelBindingAttribute.cs), which prevents model binding from accessing the form data. It's useful for cases where you know that you're going to receive large file uploads and want to prevent the form from being read into memory.
+Resource filters are useful to short-circuit most of the work a request is doing. For example, a caching filter can avoid the rest of the pipeline if the response is in the cache.
+
+The [short circuiting resource filter](#short-circuiting-resource-filter) shown earlier is one example of a resource filter. Another example is [DisableFormValueModelBindingAttribute](https://github.com/aspnet/Entropy/blob/rel/1.1.1/samples/Mvc.FileUpload/Filters/DisableFormValueModelBindingAttribute.cs):
+
+* It prevents model binding from accessing the form data.
+* It's useful for large file uploads and want to prevent the form from being read into memory.
## Action filters
-*Action filters* implement either the `IActionFilter` or `IAsyncActionFilter` interface, and their execution surrounds the execution of action methods.
+*Action filters*:
+
+* Implement either the `IActionFilter` or `IAsyncActionFilter` interface.
+* Their execution surrounds the execution of action methods.
Here's a sample action filter:
@@ -252,40 +291,57 @@ The [ActionExecutedContext](https://docs.microsoft.com/aspnet/core/api/microsoft
* `Canceled` - will be true if the action execution was short-circuited by another filter.
* `Exception` - will be non-null if the action or a subsequent action filter threw an exception. Setting this property to null effectively 'handles' an exception, and `Result` will be executed as if it were returned from the action method normally.
-For an `IAsyncActionFilter`, a call to the `ActionExecutionDelegate` executes any subsequent action filters and the action method, returning an `ActionExecutedContext`. To short-circuit, assign `ActionExecutingContext.Result` to some result instance and don't call the `ActionExecutionDelegate`.
+For an `IAsyncActionFilter`, a call to the `ActionExecutionDelegate`:
+
+* Executes any subsequent action filters and the action method.
+* returns `ActionExecutedContext`.
+
+To short-circuit, assign `ActionExecutingContext.Result` to some result instance and don't call the `ActionExecutionDelegate`.
The framework provides an abstract `ActionFilterAttribute` that you can subclass.
-You can use an action filter to automatically validate model state and return any errors if the state is invalid:
+You can use an action filter to validate model state and return any errors if the state is invalid:
[!code-csharp[](./filters/sample/src/FiltersSample/Filters/ValidateModelAttribute.cs)]
-The `OnActionExecuted` method runs after the action method and can see and manipulate the results of the action through the `ActionExecutedContext.Result` property. `ActionExecutedContext.Canceled` will be set to true if the action execution was short-circuited by another filter. `ActionExecutedContext.Exception` will be set to a non-null value if the action or a subsequent action filter threw an exception. Setting `ActionExecutedContext.Exception` to null effectively 'handles' an exception, and `ActionExectedContext.Result` will then be executed as if it were returned from the action method normally.
+The `OnActionExecuted` method runs after the action method and can see and manipulate the results of the action through the `ActionExecutedContext.Result` property. `ActionExecutedContext.Canceled` will be set to true if the action execution was short-circuited by another filter. `ActionExecutedContext.Exception` will be set to a non-null value if the action or a subsequent action filter threw an exception. Setting `ActionExecutedContext.Exception` to null:
+
+* Effectively 'handles' an exception.
+* `ActionExectedContext.Result` is executed as if it were returned normally from the action method.
## Exception filters
*Exception filters* implement either the `IExceptionFilter` or `IAsyncExceptionFilter` interface. They can be used to implement common error handling policies for an app.
-The following sample exception filter uses a custom developer error view to display details about exceptions that occur when the application is in development:
+The following sample exception filter uses a custom developer error view to display details about exceptions that occur when the app is in development:
[!code-csharp[](./filters/sample/src/FiltersSample/Filters/CustomExceptionFilterAttribute.cs?name=snippet_ExceptionFilter&highlight=1,14)]
-Exception filters don't have two events (for before and after) - they only implement `OnException` (or `OnExceptionAsync`).
+Exception filters:
-Exception filters handle unhandled exceptions that occur in controller creation, [model binding](../models/model-binding.md), action filters, or action methods. They won't catch exceptions that occur in Resource filters, Result filters, or MVC Result execution.
+* Don't have before and after events.
+* Implement `OnException` or `OnExceptionAsync`.
+* Handle unhandled exceptions that occur in controller creation, [model binding](../models/model-binding.md), action filters, or action methods.
+* Do not catch exceptions that occur in Resource filters, Result filters, or MVC Result execution.
-To handle an exception, set the `ExceptionContext.ExceptionHandled` property to true or write a response. This stops propagation of the exception. Note that an Exception filter can't turn an exception into a "success". Only an Action filter can do that.
+To handle an exception, set the `ExceptionContext.ExceptionHandled` property to true or write a response. This stops propagation of the exception. An Exception filter can't turn an exception into a "success". Only an Action filter can do that.
> [!NOTE]
> In ASP.NET Core 1.1, the response isn't sent if you set `ExceptionHandled` to true **and** write a response. In that scenario, ASP.NET Core 1.0 does send the response, and ASP.NET Core 1.1.2 will return to the 1.0 behavior. For more information, see [issue #5594](https://github.com/aspnet/Mvc/issues/5594) in the GitHub repository.
-Exception filters are good for trapping exceptions that occur within MVC actions, but they're not as flexible as error handling middleware. Prefer middleware for the general case, and use filters only where you need to do error handling *differently* based on which MVC action was chosen. For example, your app might have action methods for both API endpoints and for views/HTML. The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.
+Exception filters:
+
+* Are good for trapping exceptions that occur within MVC actions.
+* Are not as flexible as error handling middleware.
-The framework provides an abstract `ExceptionFilterAttribute` that you can subclass.
+Prefer middleware for exception handling. Use exception filters only where you need to do error handling *differently* based on which MVC action was chosen. For example, your app might have action methods for both API endpoints and for views/HTML. The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.
+
+The `ExceptionFilterAttribute` can be subclassed.
## Result filters
-*Result filters* implement either the `IResultFilter` or `IAsyncResultFilter` interface, and their execution surrounds the execution of action results.
+* Implement either the `IResultFilter` or `IAsyncResultFilter` interface.
+* Their execution surrounds the execution of action results.
Here's an example of a Result filter that adds an HTTP header.
@@ -295,13 +351,16 @@ The kind of result being executed depends on the action in question. An MVC acti
Result filters are only executed for successful results - when the action or action filters produce an action result. Result filters are not executed when exception filters handle an exception.
-The `OnResultExecuting` method can short-circuit execution of the action result and subsequent result filters by setting `ResultExecutingContext.Cancel` to true. You should generally write to the response object when short-circuiting to avoid generating an empty response. Throwing an exception will also prevent execution of the action result and subsequent filters, but will be treated as a failure instead of a successful result.
+The `OnResultExecuting` method can short-circuit execution of the action result and subsequent result filters by setting `ResultExecutingContext.Cancel` to true. You should generally write to the response object when short-circuiting to avoid generating an empty response. Throwing an exception will:
+
+* Prevent execution of the action result and subsequent filters.
+* Be treated as a failure instead of a successful result.
When the `OnResultExecuted` method runs, the response has likely been sent to the client and cannot be changed further (unless an exception was thrown). `ResultExecutedContext.Canceled` will be set to true if the action result execution was short-circuited by another filter.
`ResultExecutedContext.Exception` will be set to a non-null value if the action result or a subsequent result filter threw an exception. Setting `Exception` to null effectively 'handles' an exception and prevents the exception from being rethrown by MVC later in the pipeline. When you're handling an exception in a result filter, you might not be able to write any data to the response. If the action result throws partway through its execution, and the headers have already been flushed to the client, there's no reliable mechanism to send a failure code.
-For an `IAsyncResultFilter` a call to `await next()` on the `ResultExecutionDelegate` executes any subsequent result filters and the action result. To short-circuit, set `ResultExecutingContext.Cancel` to true and don't call the `ResultExectionDelegate`.
+For an `IAsyncResultFilter` a call to `await next` on the `ResultExecutionDelegate` executes any subsequent result filters and the action result. To short-circuit, set `ResultExecutingContext.Cancel` to true and don't call the `ResultExectionDelegate`.
The framework provides an abstract `ResultFilterAttribute` that you can subclass. The [AddHeaderAttribute](#add-header-attribute) class shown earlier is an example of a result filter attribute.
diff --git a/aspnetcore/mvc/razor-pages/filter.md b/aspnetcore/mvc/razor-pages/filter.md
new file mode 100644
index 000000000000..05930fc7d546
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter.md
@@ -0,0 +1,104 @@
+---
+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
+monikerRange: '>= aspnetcore-2.1'
+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`. For example, the [Implement a filter attribute](#ifa) sample adds a header to the response, something that can't be done with constructors or middleware.
+
+[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 `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 calls `AddFolderApplicationModelConvention` to apply the `SampleAsyncPageFilter` to only pages in */subFolder*:
+
+[!code-csharp[Main](filter/sample/PageFilter/Startup2.cs?name=snippet2)]
+
+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)]
+
+::: moniker range=">= aspnetcore-2.1"
+## Implement Razor Page filters by overriding filter methods
+
+The following code overrides the synchronous Razor Page filters:
+
+[!code-csharp[Main](filter/sample/PageFilter/Pages/Index.cshtml.cs)]
+
+::: moniker-end
+
+
+## Implement a filter attribute
+
+The built-in attribute-based filter [OnResultExecutionAsync](/dotnet/api/microsoft.aspnetcore.mvc.filters.iasyncresultfilter.onresultexecutionasync?view=aspnetcore-2.0#Microsoft_AspNetCore_Mvc_Filters_IAsyncResultFilter_OnResultExecutionAsync_Microsoft_AspNetCore_Mvc_Filters_ResultExecutingContext_Microsoft_AspNetCore_Mvc_Filters_ResultExecutionDelegate_) filter can be subclassed. The following filter adds a header to the response:
+
+[!code-csharp[Main](filter/sample/PageFilter/Filters/AddHeaderAttribute.cs)]
+
+The following code applies the `AddHeader` attribute:
+
+[!code-csharp[Main](filter/sample/PageFilter/Pages/Contact.cshtml.cs?name=snippet1)]
+
+See [Overriding the default order](xref:mvc/controllers/filters#overriding-the-default-order) for instructions on overriding the order.
+
+See [Cancellation and short circuiting](xref:mvc/controllers/filters#cancellation-and-short-circuiting) for instructions to short-circuit the filter pipeline from a filter.
+
+
+## Authorize filter attribute
+
+The [Authorize](/dotnet/api/microsoft.aspnetcore.authorization.authorizeattribute?view=aspnetcore-2.0) attribute can be applied to a `PageModel`:
+
+[!code-csharp[Main](filter/sample/PageFilter/Pages/ModelWithAuthFilter.cshtml.cs?highlight=7)]
\ No newline at end of file
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/AddHeaderAttribute.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/AddHeaderAttribute.cs
new file mode 100644
index 000000000000..f2f151a95cd1
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/AddHeaderAttribute.cs
@@ -0,0 +1,22 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace PageFilter.Filters
+{
+ public class AddHeaderAttribute : ResultFilterAttribute
+ {
+ private readonly string _name;
+ private readonly string _value;
+
+ public AddHeaderAttribute (string name, string value)
+ {
+ _name = name;
+ _value = value;
+ }
+
+ public override void OnResultExecuting(ResultExecutingContext context)
+ {
+ context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
+ }
+ }
+}
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/GlobalFiltersLogger.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/GlobalFiltersLogger.cs
new file mode 100644
index 000000000000..2e5b9cc394dc
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/GlobalFiltersLogger.cs
@@ -0,0 +1,7 @@
+// This class is used to create the logger type in Startup.
+namespace PageFilter.Filters
+{
+ public class GlobalFiltersLogger
+ {
+ }
+}
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SampleAsyncPageFilter.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SampleAsyncPageFilter.cs
new file mode 100644
index 000000000000..dd76a3243d47
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SampleAsyncPageFilter.cs
@@ -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
\ No newline at end of file
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SamplePageFilter.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SamplePageFilter.cs
new file mode 100644
index 000000000000..51ad80b1a325
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Filters/SamplePageFilter.cs
@@ -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
\ No newline at end of file
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/PageFilter.csproj b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/PageFilter.csproj
new file mode 100644
index 000000000000..bb7367e8c12d
--- /dev/null
+++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/PageFilter.csproj
@@ -0,0 +1,12 @@
+
Use this area to provide additional information.
+ +
+ Request ID: @Model.RequestId
+
+ Swapping to Development environment will display more detailed information about the error that occurred. +
++ Development environment should not be enabled in deployed applications, 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 ASPNETCORE_ENVIRONMENT environment variable to Development, and restarting the application. +
diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Error.cshtml.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Error.cshtml.cs new file mode 100644 index 000000000000..3eeafb446c62 --- /dev/null +++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Error.cshtml.cs @@ -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; + } + } +} diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml new file mode 100644 index 000000000000..8ebe0b521dd8 --- /dev/null +++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml @@ -0,0 +1,10 @@ +@page "{id:int?}" +@model IndexModel +@{ + ViewData["Title"] = "Home page"; +} + +Simplified Index page.
\ No newline at end of file diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml.cs new file mode 100644 index 000000000000..69030a7478c5 --- /dev/null +++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Index.cshtml.cs @@ -0,0 +1,42 @@ +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(ILoggerUse this page to detail your site's privacy policy.
\ No newline at end of file diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Privacy.cshtml.cs b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Privacy.cshtml.cs new file mode 100644 index 000000000000..b4f215941139 --- /dev/null +++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Privacy.cshtml.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace PageFilter.Pages +{ + public class PrivacyModel : PageModel + { + public void OnGet() + { + } + } +} \ No newline at end of file diff --git a/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Shared/_CookieConsentPartial.cshtml b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Shared/_CookieConsentPartial.cshtml new file mode 100644 index 000000000000..0360a039a88c --- /dev/null +++ b/aspnetcore/mvc/razor-pages/filter/sample/PageFilter/Pages/Shared/_CookieConsentPartial.cshtml @@ -0,0 +1,41 @@ +@using Microsoft.AspNetCore.Http.Features + +@{ + var consentFeature = Context.Features.GetsubFolder/ABout .
+ +