{"action":"created","comment":{"url":"https://api.github.com/repos/aspnet/Docs/pulls/comments/56759204","id":56759204,"diff_hunk":"@@ -1,8 +1,327 @@\n-.. include:: /../common/stub-topic.txt\n+Filters\n+=======\n \n-|stub-icon| Filters\n-===================\n+By `Steve Smith`_\n \n-.. include:: /../common/stub-notice.txt\n+*Filters* in ASP.NET MVC allow you to run code before or after a particular stage in the execution pipeline. Filters can be configured globally, per-controller, or per-action.\n \n-.. _issue: https://github.com/aspnet/Docs/issues/119\n+.. contents:: Sections\n+    :local:\n+    :depth: 1\n+\n+`View or download sample from GitHub <https://github.com/aspnet/Docs/tree/master/mvc/controllers/filters/sample>`_.\n+\n+How do filters work?\n+--------------------\n+\n+Each filter type is executed at a different stage in the pipeline, and thus has its own set of intended scenarios. Choose what type of filter to create based on the task you need it to perform, and where in the request pipeline it executes. Filters run within the MVC Action Invocation Pipeline, sometimes referred to as the *Filter Pipeline*, which runs after MVC selects the action to execute.\n+\n+.. image:: filters/_static/filter-pipeline-1.png\n+\n+Different filter types run at different points within the pipeline. Some filters, like authorization filters, only run before the next stage in the pipeline, and take no action afterward. Other filters, like action filters, can execute both before and after other parts of the pipeline execute, as shown below.\n+\n+.. image:: filters/_static/filter-pipeline-2.png\n+\n+Selecting a Filter\n+^^^^^^^^^^^^^^^^^^\n+\n+:ref:`Authorization filters <authorization-filters>` are used to determine whether the current user is authorized for the request being made.\n+\n+:ref:`Resource filters <resource-filters>` are the first filter to handle a request after authorization, and the last one to touch the request as it is leaving the filter pipeline. They're especially useful to implement caching or otherwise short-circuit the filter pipeline for performance reasons.\n+\n+:ref:`Action filters <action-filters>` wrap calls to individual action method calls, and can manipulate the arguments passed into an action as well as the response returned from it.\n+\n+:ref:`Exception filters <exception-filters>` are used to apply global policies to unhandled exceptions in the MVC app.\n+\n+:ref:`Result filters <result-filters>` wrap the execution of individual action results, but only when no exceptions have been thrown. They are ideal for logic that must surround view execution or formatter execution.\n+\n+Implementation\n+^^^^^^^^^^^^^^\n+\n+All filters support both synchronous and asynchronous implementations through different interface definitions. Choose the sync or async variant depending on the kind of task you need to perform. They are interchangeable from the framework's perspective.\n+\n+Synchronous filters define both an On\\ *Stage*\\ Executing and On\\ *Stage*\\ Executed method (with noted exceptions). The On\\ *Stage*\\ Executing method will be called before the event pipeline stage by the Stage name, and the On\\ *Stage*\\ Executed method will be called after the pipeline stage named by the Stage name.\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/SampleActionFilter.cs\n+  :language: c#\n+  :emphasize-lines: 6,8,13\n+\n+Asynchronous filters define a single On\\ *Stage*\\ ExecutionAsync method that will surround execution of the pipeline stage named by Stage. The On\\ *Stage*\\ ExecutionAsync method is provided a *Stage*\\ ExecutionDelegate delegate which will execute the pipeline stage named by Stage when invoked and awaited.\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/SampleAsyncActionFilter.cs\n+  :language: c#\n+  :emphasize-lines: 6,8-9\n+\n+.. note:: You should only implement *either* the synchronous or the async version of a filter interface, not both. If you need to perform async work in the filter, implement the async interface. Otherwise, implement the synchronous interface. The framework will check to see if the filter implements the async interface first, and if so, it will call it. If not, it will call the synchronous interface's method(s). If you were to implement both interfaces on one class, only the async method would be called by the framework. Also, it doesn't matter whether your action is async or not, your filters can be synchronous or async independent of the action.\n+\n+Filters and Attributes\n+^^^^^^^^^^^^^^^^^^^^^^\n+It's often convenient to implement filter interfaces as *Attributes*. Filter attributes are applied to controllers and action methods. The framework includes built-in attribute-based filters that you can subclass and customize. For example, the following filter inherits from `ResultFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/ResultFilterAttribute/index.html>`_, and overrides its ``OnResultExecuting`` method to add a header to the response.\n+\n+.. _add-header-attribute:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/AddHeaderAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 5,16\n+\n+Attributes allow filters to accept arguments, as shown in the example above. You would add this attribute to a controller or action method and specify the name and value of the HTTP header you wished to add to the response:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/SampleController.cs\n+  :language: c#\n+  :emphasize-lines: 1\n+  :lines: 6-12,25\n+  :dedent: 4\n+\n+The result of the ``Index`` action is shown below - the response headers are displayed on the bottom right.\n+\n+.. image:: filters/_static/add-header.png\n+\n+Several of the filter interfaces have corresponding attributes that can be used as base classes for custom implementations.\n+\n+Filter attributes:\n+\t- `ActionFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/ActionFilterAttribute/index.html>`_\n+\t- `AuthorizationFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/AuthorizationFilterAttribute/index.html>`_\n+\t- `ExceptionFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/ExceptionFilterAttribute/index.html>`_\n+\t- `ResultFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/ResultFilterAttribute/index.html>`_\n+\n+Dependency Injection\n+^^^^^^^^^^^^^^^^^^^^\n+Filters are not directly returned from :doc:`dependency injection </fundamentals/dependency-injection>` (DI). Filters that are implemented as attributes and added directly to controller classes or action methods cannot have constructor dependencies provided by DI. This is because attributes must have their constructor parameters supplied where they are applied. This is a limitation of how attributes work.\n+\n+However, if your filters have dependencies you need to access from DI, there are several supported approaches. You can apply your filter to a class or action method using\n+\n+\t- The `ServiceFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/ServiceFilterAttribute/index.html>`_ \n+\t- The `TypeFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/TypeFilterAttribute/index.html>`_ attribute\n+\t- `IFilterFactory <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/IFilterFactory/index.html>`__ implemented on your attribute\n+\n+.. note:: Some of the filters shown in these examples implement logging in order to demonstrate when, where, or how they are executing. Avoid creating and using filters purely for logging purposes, since the :doc:`built-in framework logging features </fundamentals/logging>` should already provide what you need for logging. If you're going to add logging to your filters, it should focus on business domain concerns or behavior specific to your filter, rather than MVC actions or other framework events.  \n+  \n+Cancellation and Short Circuiting\n+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n+You can short-circuit the filter pipeline at any point by setting the ``Result`` property on the context parameter provided to the filter method. For instance, the following ``ShortCircuitingResourceFilter`` will prevent any other filters from running later in the pipeline, including any action filters.\n+\n+.. _short-circuiting-resource-filter:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/ShortCircuitingResourceFilterAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 12-15\n+\n+In the following code, both the ``ShortCircuitingResourceFilter`` and the ``AddHeader`` filter target the ``SomeResource`` action method. However, because the ``ShortCircuitingResourceFilter`` runs first 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 (see :ref:`Ordering <ordering>`).\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/SampleController.cs\n+  :language: c#\n+  :emphasize-lines: 1,4\n+  :lines: 6-8, 14-19\n+  :dedent: 4 \n+\n+Configuring Filters\n+-------------------\n+Filters can be *scoped* at three different levels. You can add a particular filter to a particular action as an attribute. You can add a filter to all actions within a controller by applying an attribute at the controller level. Or you can register a filter globally, to be run with every MVC action.\n+\n+Global filters are added in the ``ConfigureServices`` method in ``Startup``, when configuring MVC:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Startup.cs\n+  :language: c#\n+  :emphasize-lines: 5-7\n+  :lines: 13-22\n+  :dedent: 8\n+\n+Filters can be added by type, or an instance can be added. If you add an instance, that instance will be used for every request. If you add a type, it will be type-activated, meaning the instance will be created through DI, and any constructor dependencies will be populated by DI. Adding a filter by type is equivalent to ``filters.Add(new TypeFilter(typeof(MyFilter)))``. In the example above, the :ref:`DurationActionFilter <duration-action-filter>` has a dependency on ``ILoggerFactory`` in its constructor, which is fulfilled by DI on each request.\n+\n+To create a filter *without* global scope that requires dependencies from DI, apply the ``ServiceFilterAttribute`` or ``TypeFilterAttribute`` attribute to the controller or action. A ``TypeFilter`` will instantiate an instance, using services from DI for its dependencies. A ``ServiceFilter`` retrieves an instance of the filter from DI. The following example demonstrates using a ``ServiceFilter``:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/HomeController.cs\n+  :language: c#\n+  :emphasize-lines: 1\n+  :lines: 19-24\n+  :dedent: 8\n+\n+Using ``ServiceFilter`` without registering the filter type in ``ConfigureServices``, throws the following exception:\n+\n+.. code-block: c#\n+\n+\tSystem.InvalidOperationException: No service for type 'FiltersSample.Filters.LoggingAddHeaderFilter' has been registered.\n+\n+To avoid this exception, you must register the ``LoggingAddHeaderFilter`` type in ``ConfigureServices``:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Startup.cs\n+  :language: c#\n+  :emphasize-lines: 1\n+  :lines: 21\n+  :dedent: 12\n+\n+``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).\n+\n+``TypeFilterAttribute`` is very similar to ``ServiceFilterAttribute`` (and also implements ``IFilterFactory``), but its type is not resolved directly from the DI container. Instead, it instantiates the type using an `ObjectFactory delegate <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/Extensions/DependencyInjection/ObjectFactory/index.html>`_. Because of this difference, types that are referenced using the ``TypeFilterAttribute`` do not 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``:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/HomeController.cs\n+  :language: c#\n+  :emphasize-lines: 1-2\n+  :lines: 33-39\n+  :dedent: 8\n+\n+If you have a simple filter that doesn't require any arguments, but which has constructor dependencies that need to be filled by DI, you can inherit from ``TypeFilterAttribute``, allowing you to use your own named attribute on classes and methods (instead of ``[TypeFilter(typeof(FilterType))]``). The following filter shows how this can be implemented:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/SampleLoggingActionFilterAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 1, 3, 7\n+  :lines: 8-38\n+  :dedent: 4\n+\n+This filter can be applied to classes or methods using the ``[SampleLoggingActionFilter]`` syntax.\n+\n+``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, 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 does not need to be set explicitly when the application starts.\n+\n+You can implement ``IFilterFactory`` on your own attribute implementations as another approach to creating filters:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/AddHeaderWithFactoryAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 1,4-7\n+  :lines: 6-26\n+  :dedent: 4\n+\n+.. _ordering:\n+\n+Ordering\n+^^^^^^^^\n+Filters can be applied to action methods or controllers (via attribute) or added to the global filters collection. Scope also generally determines ordering. The filter closest to the action runs first; generally you get overriding behavior without having to explicitly set ordering. This is sometimes referred to as \"Russian doll\" nesting, as each increase in scope is wrapped around the previous scope, like a `nesting doll <https://en.wikipedia.org/wiki/Matryoshka_doll>`_.\n+\n+In addition to scope, filters can override their sequence of execution by implementing `IOrderedFilter <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/IOrderedFilter/index.html>`_. This interface simply exposes an ``int`` ``Order`` property, and filters execute in ascending numeric order based on this property. All of the built-in filters, including ``TypeFilterAttribute`` and ``ServiceFilterAttribute``, implement ``IOrderedFilter``, so you can specify the order of filters when you apply the attribute to a class or method. By default, the ``Order`` property is 0 for all of the built-in filters, so scope is used as a tie-breaker and (unless ``Order`` is set to a non-zero value) is the determining factor.\n+\n+To see the scope-based ordering in action, consider the following class, which has the same filter applied at both the class and method level. The same filter is also registered globally. When run, the log output will show the order in which the filters executed, and how these compared to the ``Controller`` base class's ``OnActionExecuting`` and ``OnActionExecuted`` methods.\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/OrderingController.cs\n+  :language: c#\n+  :emphasize-lines: 1,10,18,24\n+  :lines: 8-34\n+  :dedent: 4\n+\n+The resulting order is:\n+\t#. The Controller ``OnActionExecuting``\n+\t#. The Global filter ``OnActionExecuting``\n+\t#. The Class filter ``OnActionExecuting``\n+\t#. The Method filter ``OnActionExecuting``\n+\t#. The Method filter ``OnActionExecuted``\n+\t#. The Class filter ``OnActionExecuted``\n+\t#. The Global filter ``OnActionExecuted``\n+\t#. The Controller ``OnActionExecuted``\n+\n+.. note:: ``IOrderedFilter`` trumps scope when determining the order in which filters will run. Filters are sorted first by order, then scope is used to break ties. Order defaults to 0 if not set.\n+\n+To modify the default, scope-based order, you could explicitly set the ``Order`` property of the class-level and/or method-level filters. For example, adding ``Order=-1`` to the action method:\n+\n+.. code-block:: c#\n+\n+\t[OrderLoggingActionFilter(Name = \"Method Level Attribute\", Order=-1)]\n+\n+In this case, a value of less than zero would ensure this filter ran before both the Global and Class level filters.\n+\n+The new order would be:\n+\t#. The Controller ``OnActionExecuting``\n+\t#. The Method filter ``OnActionExecuting``\n+\t#. The Global filter ``OnActionExecuting``\n+\t#. The Class filter ``OnActionExecuting``\n+\t#. The Class filter ``OnActionExecuted``\n+\t#. The Global filter ``OnActionExecuted``\n+\t#. The Method filter ``OnActionExecuted``\n+\t#. The Controller ``OnActionExecuted``\n+\n+.. note:: The ``Controller`` class's methods always run before and after all filters. These methods are not implemented as ``IFilter`` instances and do not participate in the ``IFilter`` ordering algorithm.\n+\n+.. _authorization-filters:\n+\n+Authorization Filters\n+---------------------\n+*Authorization Filters* control access to action methods, and are the first filters to be executed within the filter pipeline. They have only a before stage, 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. Note that you should not 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. They are covered in the :doc:`Security </security/index>` section of the documentation.\n+\n+Learn more about :doc:`/security/authorization/authorization-filters`.\n+\n+.. _resource-filters:\n+\n+Resource Filters\n+----------------\n+*Resource Filters* implement either the ``IResourceFilter`` or ``IAsyncResourceFilter`` interface, and their execution wraps most of the filter pipeline (only :ref:`authorization-filters` run before them - all other filters and action processing happens between their ``OnResourceExecuting`` and ``OnResourceExecuted`` methods). Resource filters are especially useful if you need to short-circuit most of the work a request is doing. Caching would be one example use case for a resource filter, since if the response is already in the cache, the filter can immediately set a result and avoid the rest of the processing for the action.\n+\n+The :ref:`short circuiting resource filter <short-circuiting-resource-filter>` shown above is one example of a resource filter. A very naive cache implementation (do not use this in production) is shown below:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/NaiveCacheResourceFilterAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 1-2,11,16,24,26\n+  :lines: 8-36\n+  :dedent: 4\n+\n+In ``OnResourceExecuting``, if the result is already in the static dictionary cache, the ``Result`` property is set on ``context``, and the action short-circuits and returns with the cached result. In the ``OnResourceExecuted`` method, if the current request's key isn't already in use, the current ``Result`` is stored in the cache, to be used by future requests.\n+\n+Adding this filter to a class or method is shown here:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Controllers/CachedController.cs\n+  :language: c#\n+  :emphasize-lines: 1-2,6\n+  :lines: 7-14\n+  :dedent: 4\n+  \n+.. _action-filters:\n+\n+Action Filters\n+--------------\n+*Action Filters* implement either the ``IActionFilter`` or ``IAsyncActionFilter`` interface and their execution surrounds the execution of action methods. Action filters are ideal for any logic that needs to see the results of model binding, or modify the controller or inputs to an action method. Additionally, action filters can view and directly modify the result of an action method.\n+\n+The ``OnActionExecuting`` method runs before the action method, so it can manipulate the inputs to the action by changing ``ActionExecutingContext.ActionArguments`` or manipulate the controller through ``ActionExecutingContext.Controller``. An ``OnActionExecuting`` method can short-circuit execution of the action method and subsequent action filters by setting ``ActionExecutingContext.Result``. Throwing an exception in an ``OnActionExecuting`` method will also prevent execution of the action method and subsequent filters, but will be treated as a failure instead of successful result.\n+\n+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.\n+\n+For an ``IAsyncActionFilter`` the ``OnActionExecutionAsync`` combines all the possibilities of ``OnActionExecuting`` and ``OnActionExecuted``. A call to ``await next()`` on the ``ActionExecutionDelegate`` will execute any subsequent action filters and the action method, returning an ``ActionExecutedContext``. To short-circuit inside of an ``OnActionExecutionAsync``, assign ``ActionExecutingContext.Result`` to some result instance and do not call the ``ActionExectionDelegate``.\n+\n+The ``DurationActionFilter`` shown below uses a ``Stopwatch`` to measure the execution time of the action, for display on the page. The ``ViewData`` property of the ``ViewResult`` can be accessed and manipulated, including adding new values.\n+\n+.. _duration-action-filter:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/DurationActionFilter.cs\n+  :language: c#\n+  :emphasize-lines: 1,21-26\n+  :lines: 8-35\n+  :dedent: 4\n+\n+.. note:: You don't need to use this filter to log action times unless you need to display it on a page. The framework's built-in :doc:`logging </fundamentals/logging>` already logs action timing information.\n+\n+.. _exception-filters:\n+\n+Exception Filters\n+-----------------\n+*Exception Filters* implement either the ``IExceptionFilter`` or ``IAsyncExceptionFilter`` interface.\n+\n+Exception filters handle unhandled exceptions. They are only called when an exception occurs later in the pipeline. They can provide a single location to implement common error handling policies within an app. The framework provides an abstract `ExceptionFilterAttribute <https://docs.asp.net/projects/api/en/latest/autoapi/Microsoft/AspNet/Mvc/Filters/ExceptionFilterAttribute/index.html>`_ that you should be able to subclass for your needs. 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.\n+\n+.. tip:: One example where you might need a different form of error handling for different actions would be in an app that exposes both API endpoints and actions that return views/HTML. The API endpoints could return error information as JSON, while the view-based actions could return an error page as HTML.\n+\n+Exception filters do not have two events (for before and after) - they only implement ``OnException`` (or ``OnExceptionAsync``). The ``ExceptionContext`` provided in the ``OnException`` parameter includes the ``Exception`` that occurred. If you set ``context.Exception`` to null, the effect is that you've handled the exception, so the request will proceed as if it hadn't occurred (generally returning a 200 OK status). The following filter logs exceptions and marks them as handled:\n+\n+.. literalinclude:: filters/sample/src/FiltersSample/Filters/LoggingExceptionFilterAttribute.cs\n+  :language: c#\n+  :emphasize-lines: 7,15,18\n+\n+.. _result-filters:\n+\n+Result Filters\n+--------------\n+*Result Filters* implement either the ``IResultFilter`` or ``IAsyncResultFilter`` interface and their execution surrounds the execution of action results. 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, unless the exception filter sets ``Exception = null``.\n+\n+.. note:: The kind of result being executed depends on the action in question. An MVC action returning a view would include all razor processing as part of the ``ViewResult`` being executed. An API method might perform some serialization as part of the execution of the result.\n+\n+Result filters are ideal for any logic that needs to directly surround view execution or formatter execution. Result filters can replace or modify the action result that's responsible for producing the response.","path":"aspnet/mvc/controllers/filters.rst","position":316,"original_position":316,"commit_id":"9842a2b127e08233eed4450a24618efa0dbecf45","original_commit_id":"9842a2b127e08233eed4450a24618efa0dbecf45","user":{"login":"danroth27","id":1874516,"avatar_url":"https://avatars.githubusercontent.com/u/1874516?v=3","gravatar_id":"","url":"https://api.github.com/users/danroth27","html_url":"https://github.com/danroth27","followers_url":"https://api.github.com/users/danroth27/followers","following_url":"https://api.github.com/users/danroth27/following{/other_user}","gists_url":"https://api.github.com/users/danroth27/gists{/gist_id}","starred_url":"https://api.github.com/users/danroth27/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danroth27/subscriptions","organizations_url":"https://api.github.com/users/danroth27/orgs","repos_url":"https://api.github.com/users/danroth27/repos","events_url":"https://api.github.com/users/danroth27/events{/privacy}","received_events_url":"https://api.github.com/users/danroth27/received_events","type":"User","site_admin":false},"body":"Some examples we have in the product are setting the content-type header based on something in the request, like a format query string parameter, or restricting the response content-type to be one of a specified set of media types.","created_at":"2016-03-20T04:36:42Z","updated_at":"2016-03-20T04:36:42Z","html_url":"https://github.com/aspnet/Docs/pull/1087#discussion_r56759204","pull_request_url":"https://api.github.com/repos/aspnet/Docs/pulls/1087","_links":{"self":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/comments/56759204"},"html":{"href":"https://github.com/aspnet/Docs/pull/1087#discussion_r56759204"},"pull_request":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/1087"}}},"pull_request":{"url":"https://api.github.com/repos/aspnet/Docs/pulls/1087","id":62546220,"html_url":"https://github.com/aspnet/Docs/pull/1087","diff_url":"https://github.com/aspnet/Docs/pull/1087.diff","patch_url":"https://github.com/aspnet/Docs/pull/1087.patch","issue_url":"https://api.github.com/repos/aspnet/Docs/issues/1087","number":1087,"state":"open","locked":false,"title":"Filters","user":{"login":"ardalis","id":782127,"avatar_url":"https://avatars.githubusercontent.com/u/782127?v=3","gravatar_id":"","url":"https://api.github.com/users/ardalis","html_url":"https://github.com/ardalis","followers_url":"https://api.github.com/users/ardalis/followers","following_url":"https://api.github.com/users/ardalis/following{/other_user}","gists_url":"https://api.github.com/users/ardalis/gists{/gist_id}","starred_url":"https://api.github.com/users/ardalis/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ardalis/subscriptions","organizations_url":"https://api.github.com/users/ardalis/orgs","repos_url":"https://api.github.com/users/ardalis/repos","events_url":"https://api.github.com/users/ardalis/events{/privacy}","received_events_url":"https://api.github.com/users/ardalis/received_events","type":"User","site_admin":false},"body":"Ready for review @rynowak @Rick-Anderson ","created_at":"2016-03-11T13:59:04Z","updated_at":"2016-03-20T04:36:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"19d3f5a0840857512ddaf9622b5a2a4bc7360f40","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/aspnet/Docs/pulls/1087/commits","review_comments_url":"https://api.github.com/repos/aspnet/Docs/pulls/1087/comments","review_comment_url":"https://api.github.com/repos/aspnet/Docs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aspnet/Docs/issues/1087/comments","statuses_url":"https://api.github.com/repos/aspnet/Docs/statuses/9842a2b127e08233eed4450a24618efa0dbecf45","head":{"label":"aspnet:ardalis/filters","ref":"ardalis/filters","sha":"9842a2b127e08233eed4450a24618efa0dbecf45","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":28239455,"name":"Docs","full_name":"aspnet/Docs","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Docs","description":"Documentation for ASP.NET 5","fork":false,"url":"https://api.github.com/repos/aspnet/Docs","forks_url":"https://api.github.com/repos/aspnet/Docs/forks","keys_url":"https://api.github.com/repos/aspnet/Docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Docs/teams","hooks_url":"https://api.github.com/repos/aspnet/Docs/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Docs/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Docs/events","assignees_url":"https://api.github.com/repos/aspnet/Docs/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Docs/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Docs/tags","blobs_url":"https://api.github.com/repos/aspnet/Docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Docs/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Docs/languages","stargazers_url":"https://api.github.com/repos/aspnet/Docs/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Docs/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Docs/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Docs/subscription","commits_url":"https://api.github.com/repos/aspnet/Docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/Docs/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Docs/merges","archive_url":"https://api.github.com/repos/aspnet/Docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Docs/downloads","issues_url":"https://api.github.com/repos/aspnet/Docs/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Docs/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Docs/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Docs/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Docs/releases{/id}","deployments_url":"https://api.github.com/repos/aspnet/Docs/deployments","created_at":"2014-12-19T17:38:02Z","updated_at":"2016-03-19T08:10:40Z","pushed_at":"2016-03-18T23:11:56Z","git_url":"git://github.com/aspnet/Docs.git","ssh_url":"git@github.com:aspnet/Docs.git","clone_url":"https://github.com/aspnet/Docs.git","svn_url":"https://github.com/aspnet/Docs","homepage":null,"size":43298,"stargazers_count":421,"watchers_count":421,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":686,"mirror_url":null,"open_issues_count":156,"forks":686,"open_issues":156,"watchers":421,"default_branch":"master"}},"base":{"label":"aspnet:master","ref":"master","sha":"4d185ba1d9cc10d8f6a8ea7a0642016344580024","user":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"repo":{"id":28239455,"name":"Docs","full_name":"aspnet/Docs","owner":{"login":"aspnet","id":6476660,"avatar_url":"https://avatars.githubusercontent.com/u/6476660?v=3","gravatar_id":"","url":"https://api.github.com/users/aspnet","html_url":"https://github.com/aspnet","followers_url":"https://api.github.com/users/aspnet/followers","following_url":"https://api.github.com/users/aspnet/following{/other_user}","gists_url":"https://api.github.com/users/aspnet/gists{/gist_id}","starred_url":"https://api.github.com/users/aspnet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aspnet/subscriptions","organizations_url":"https://api.github.com/users/aspnet/orgs","repos_url":"https://api.github.com/users/aspnet/repos","events_url":"https://api.github.com/users/aspnet/events{/privacy}","received_events_url":"https://api.github.com/users/aspnet/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aspnet/Docs","description":"Documentation for ASP.NET 5","fork":false,"url":"https://api.github.com/repos/aspnet/Docs","forks_url":"https://api.github.com/repos/aspnet/Docs/forks","keys_url":"https://api.github.com/repos/aspnet/Docs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aspnet/Docs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aspnet/Docs/teams","hooks_url":"https://api.github.com/repos/aspnet/Docs/hooks","issue_events_url":"https://api.github.com/repos/aspnet/Docs/issues/events{/number}","events_url":"https://api.github.com/repos/aspnet/Docs/events","assignees_url":"https://api.github.com/repos/aspnet/Docs/assignees{/user}","branches_url":"https://api.github.com/repos/aspnet/Docs/branches{/branch}","tags_url":"https://api.github.com/repos/aspnet/Docs/tags","blobs_url":"https://api.github.com/repos/aspnet/Docs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aspnet/Docs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aspnet/Docs/git/refs{/sha}","trees_url":"https://api.github.com/repos/aspnet/Docs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aspnet/Docs/statuses/{sha}","languages_url":"https://api.github.com/repos/aspnet/Docs/languages","stargazers_url":"https://api.github.com/repos/aspnet/Docs/stargazers","contributors_url":"https://api.github.com/repos/aspnet/Docs/contributors","subscribers_url":"https://api.github.com/repos/aspnet/Docs/subscribers","subscription_url":"https://api.github.com/repos/aspnet/Docs/subscription","commits_url":"https://api.github.com/repos/aspnet/Docs/commits{/sha}","git_commits_url":"https://api.github.com/repos/aspnet/Docs/git/commits{/sha}","comments_url":"https://api.github.com/repos/aspnet/Docs/comments{/number}","issue_comment_url":"https://api.github.com/repos/aspnet/Docs/issues/comments{/number}","contents_url":"https://api.github.com/repos/aspnet/Docs/contents/{+path}","compare_url":"https://api.github.com/repos/aspnet/Docs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aspnet/Docs/merges","archive_url":"https://api.github.com/repos/aspnet/Docs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aspnet/Docs/downloads","issues_url":"https://api.github.com/repos/aspnet/Docs/issues{/number}","pulls_url":"https://api.github.com/repos/aspnet/Docs/pulls{/number}","milestones_url":"https://api.github.com/repos/aspnet/Docs/milestones{/number}","notifications_url":"https://api.github.com/repos/aspnet/Docs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aspnet/Docs/labels{/name}","releases_url":"https://api.github.com/repos/aspnet/Docs/releases{/id}","deployments_url":"https://api.github.com/repos/aspnet/Docs/deployments","created_at":"2014-12-19T17:38:02Z","updated_at":"2016-03-19T08:10:40Z","pushed_at":"2016-03-18T23:11:56Z","git_url":"git://github.com/aspnet/Docs.git","ssh_url":"git@github.com:aspnet/Docs.git","clone_url":"https://github.com/aspnet/Docs.git","svn_url":"https://github.com/aspnet/Docs","homepage":null,"size":43298,"stargazers_count":421,"watchers_count":421,"language":"C#","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":686,"mirror_url":null,"open_issues_count":156,"forks":686,"open_issues":156,"watchers":421,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/1087"},"html":{"href":"https://github.com/aspnet/Docs/pull/1087"},"issue":{"href":"https://api.github.com/repos/aspnet/Docs/issues/1087"},"comments":{"href":"https://api.github.com/repos/aspnet/Docs/issues/1087/comments"},"review_comments":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/1087/comments"},"review_comment":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aspnet/Docs/pulls/1087/commits"},"statuses":{"href":"https://api.github.com/repos/aspnet/Docs/statuses/9842a2b127e08233eed4450a24618efa0dbecf45"}}}}