diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/Filters/ResourceExecutingContext.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Filters/ResourceExecutingContext.cs
index 30281025da..f35aab6d79 100644
--- a/src/Microsoft.AspNet.Mvc.Abstractions/Filters/ResourceExecutingContext.cs
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/Filters/ResourceExecutingContext.cs
@@ -27,12 +27,12 @@ namespace Microsoft.AspNet.Mvc.Filters
///
/// Gets or sets the list of instances used by model binding.
///
- public virtual IList InputFormatters { get; set; }
+ public virtual FormatterCollection InputFormatters { get; set; }
///
/// Gets or sets the list of instances used to format the response.
///
- public virtual IList OutputFormatters { get; set; }
+ public virtual FormatterCollection OutputFormatters { get; set; }
///
/// Gets or sets the list of instances used by model binding.
@@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.Filters
public virtual IList ModelBinders { get; set; }
///
- /// Gets or sets the result of the action to be executed.
+ /// Gets or sets the result of the action to be executed.
///
///
/// Setting to a non-null value inside a resource filter will
diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/FormatterCollection.cs b/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/FormatterCollection.cs
similarity index 60%
rename from src/Microsoft.AspNet.Mvc.Core/Formatters/FormatterCollection.cs
rename to src/Microsoft.AspNet.Mvc.Abstractions/Formatters/FormatterCollection.cs
index 005c0ab677..1daa7112ee 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Formatters/FormatterCollection.cs
+++ b/src/Microsoft.AspNet.Mvc.Abstractions/Formatters/FormatterCollection.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Microsoft.AspNet.Mvc.Formatters
@@ -11,6 +12,23 @@ namespace Microsoft.AspNet.Mvc.Formatters
/// The type of formatters in the collection.
public class FormatterCollection : Collection
{
+ ///
+ /// Initializes a new instance of the class that is empty.
+ ///
+ public FormatterCollection()
+ : base()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// as a wrapper for the specified list.
+ ///
+ /// The list that is wrapped by the new collection.
+ public FormatterCollection(IList list)
+ : base(list)
+ {
+ }
///
/// Removes all formatters of the specified type.
diff --git a/src/Microsoft.AspNet.Mvc.Core/Controllers/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/Controllers/FilterActionInvoker.cs
index 3bc87fff0f..27a1e40d7c 100644
--- a/src/Microsoft.AspNet.Mvc.Core/Controllers/FilterActionInvoker.cs
+++ b/src/Microsoft.AspNet.Mvc.Core/Controllers/FilterActionInvoker.cs
@@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
private ResultExecutingContext _resultExecutingContext;
private ResultExecutedContext _resultExecutedContext;
-
+
public FilterActionInvoker(
ActionContext actionContext,
IReadOnlyList filterProviders,
@@ -334,8 +334,10 @@ namespace Microsoft.AspNet.Mvc.Controllers
var context = new ResourceExecutingContext(ActionContext, _filters);
- context.InputFormatters = new CopyOnWriteList(_inputFormatters);
- context.OutputFormatters = new CopyOnWriteList(_outputFormatters);
+ context.InputFormatters = new FormatterCollection(
+ new CopyOnWriteList(_inputFormatters));
+ context.OutputFormatters = new FormatterCollection(
+ new CopyOnWriteList(_outputFormatters));
context.ModelBinders = new CopyOnWriteList(_modelBinders);
context.ValidatorProviders = new CopyOnWriteList(_modelValidatorProviders);
context.ValueProviderFactories = new CopyOnWriteList(_valueProviderFactories);
@@ -411,7 +413,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
{
// Short-circuited by setting a result.
Logger.ResourceFilterShortCircuited(item.Filter);
-
+
await InvokeResultAsync(_resourceExecutingContext.Result);
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/BasicTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/BasicTests.cs
index a2f847358b..f9b2a21629 100644
--- a/test/Microsoft.AspNet.Mvc.FunctionalTests/BasicTests.cs
+++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/BasicTests.cs
@@ -7,6 +7,7 @@ using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
+using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@@ -346,5 +347,34 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
// Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
+
+ [Fact]
+ public async Task ResourceFilter_CreatesSpecificInputAndOutputFormatters()
+ {
+ // Arrange
+ var input = "{\"fullName\":\"John Doe\"}";
+
+ // Act
+ var response = await Client.PostAsync(
+ "http://localhost/api/ActionUsingSpecificFormatters",
+ new StringContent(input, Encoding.UTF8, "application/json"));
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(input, await response.Content.ReadAsStringAsync());
+
+ // Make sure it does not affect other actions
+ //Arrange
+ input = "{\"FullName\":\"John Doe\"}";
+
+ // Act
+ response = await Client.PostAsync(
+ "http://localhost/api/ActionUsingGlobalFormatters",
+ new StringContent(input, Encoding.UTF8, "application/json"));
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(input, await response.Content.ReadAsStringAsync());
+ }
}
}
\ No newline at end of file
diff --git a/test/WebSites/BasicWebSite/Controllers/SpecificFormattersController.cs b/test/WebSites/BasicWebSite/Controllers/SpecificFormattersController.cs
new file mode 100644
index 0000000000..1cadb3e0fe
--- /dev/null
+++ b/test/WebSites/BasicWebSite/Controllers/SpecificFormattersController.cs
@@ -0,0 +1,71 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.AspNet.Mvc;
+using Microsoft.AspNet.Mvc.Filters;
+using Microsoft.AspNet.Mvc.Formatters;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+
+namespace BasicWebSite
+{
+ public class SpecificFormattersController : Controller
+ {
+ [HttpPost("api/ActionUsingSpecificFormatters")]
+ [CamelCaseJsonFormatters]
+ public IActionResult ActionUsingSpecificFormatters([FromBody] Person person)
+ {
+ if (!ModelState.IsValid)
+ {
+ return HttpBadRequest(ModelState);
+ }
+
+ return Ok(person);
+ }
+
+ [HttpPost("api/ActionUsingGlobalFormatters")]
+ public IActionResult ActionUsingGlobalFormatters([FromBody] Person person)
+ {
+ if (!ModelState.IsValid)
+ {
+ return HttpBadRequest(ModelState);
+ }
+
+ return Ok(person);
+ }
+
+ public class Person
+ {
+ public string FullName { get; set; }
+ }
+
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+ private class CamelCaseJsonFormattersAttribute : Attribute, IResourceFilter
+ {
+ private readonly JsonSerializerSettings _serializerSettings;
+
+ public CamelCaseJsonFormattersAttribute()
+ {
+ _serializerSettings = new JsonSerializerSettings();
+ _serializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
+ }
+
+ public void OnResourceExecuted(ResourceExecutedContext context)
+ {
+ }
+
+ public void OnResourceExecuting(ResourceExecutingContext context)
+ {
+ // Do not modify existing json formatters as they would effect all controllers.
+ // Instead remove and add new formatters which only effects the controllers this
+ // attribute is decorated on.
+ context.InputFormatters.RemoveType();
+ context.OutputFormatters.RemoveType();
+
+ context.InputFormatters.Add(new JsonInputFormatter(_serializerSettings));
+ context.OutputFormatters.Add(new JsonOutputFormatter(_serializerSettings));
+ }
+ }
+ }
+}