[Fixes #3503] Removing formatters on a given type only works on collections in MVC options
This commit is contained in:
parent
a65a6a7cab
commit
982c7abea8
|
|
@ -27,12 +27,12 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="IInputFormatter"/> instances used by model binding.
|
/// Gets or sets the list of <see cref="IInputFormatter"/> instances used by model binding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual IList<IInputFormatter> InputFormatters { get; set; }
|
public virtual FormatterCollection<IInputFormatter> InputFormatters { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="IOutputFormatter"/> instances used to format the response.
|
/// Gets or sets the list of <see cref="IOutputFormatter"/> instances used to format the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual IList<IOutputFormatter> OutputFormatters { get; set; }
|
public virtual FormatterCollection<IOutputFormatter> OutputFormatters { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list of <see cref="IModelBinder"/> instances used by model binding.
|
/// Gets or sets the list of <see cref="IModelBinder"/> instances used by model binding.
|
||||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNet.Mvc.Filters
|
||||||
public virtual IList<IModelBinder> ModelBinders { get; set; }
|
public virtual IList<IModelBinder> ModelBinders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the result of the action to be executed.
|
/// Gets or sets the result of the action to be executed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Setting <see cref="Result"/> to a non-<c>null</c> value inside a resource filter will
|
/// Setting <see cref="Result"/> to a non-<c>null</c> value inside a resource filter will
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Formatters
|
namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
|
|
@ -11,6 +12,23 @@ namespace Microsoft.AspNet.Mvc.Formatters
|
||||||
/// <typeparam name="TFormatter">The type of formatters in the collection.</typeparam>
|
/// <typeparam name="TFormatter">The type of formatters in the collection.</typeparam>
|
||||||
public class FormatterCollection<TFormatter> : Collection<TFormatter>
|
public class FormatterCollection<TFormatter> : Collection<TFormatter>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FormatterCollection{TFormatter}"/> class that is empty.
|
||||||
|
/// </summary>
|
||||||
|
public FormatterCollection()
|
||||||
|
: base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="FormatterCollection{TFormatter}"/> class
|
||||||
|
/// as a wrapper for the specified list.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list">The list that is wrapped by the new collection.</param>
|
||||||
|
public FormatterCollection(IList<TFormatter> list)
|
||||||
|
: base(list)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes all formatters of the specified type.
|
/// Removes all formatters of the specified type.
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
private ResultExecutingContext _resultExecutingContext;
|
private ResultExecutingContext _resultExecutingContext;
|
||||||
private ResultExecutedContext _resultExecutedContext;
|
private ResultExecutedContext _resultExecutedContext;
|
||||||
|
|
||||||
public FilterActionInvoker(
|
public FilterActionInvoker(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
IReadOnlyList<IFilterProvider> filterProviders,
|
IReadOnlyList<IFilterProvider> filterProviders,
|
||||||
|
|
@ -334,8 +334,10 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
|
|
||||||
var context = new ResourceExecutingContext(ActionContext, _filters);
|
var context = new ResourceExecutingContext(ActionContext, _filters);
|
||||||
|
|
||||||
context.InputFormatters = new CopyOnWriteList<IInputFormatter>(_inputFormatters);
|
context.InputFormatters = new FormatterCollection<IInputFormatter>(
|
||||||
context.OutputFormatters = new CopyOnWriteList<IOutputFormatter>(_outputFormatters);
|
new CopyOnWriteList<IInputFormatter>(_inputFormatters));
|
||||||
|
context.OutputFormatters = new FormatterCollection<IOutputFormatter>(
|
||||||
|
new CopyOnWriteList<IOutputFormatter>(_outputFormatters));
|
||||||
context.ModelBinders = new CopyOnWriteList<IModelBinder>(_modelBinders);
|
context.ModelBinders = new CopyOnWriteList<IModelBinder>(_modelBinders);
|
||||||
context.ValidatorProviders = new CopyOnWriteList<IModelValidatorProvider>(_modelValidatorProviders);
|
context.ValidatorProviders = new CopyOnWriteList<IModelValidatorProvider>(_modelValidatorProviders);
|
||||||
context.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);
|
context.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories);
|
||||||
|
|
@ -411,7 +413,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
// Short-circuited by setting a result.
|
// Short-circuited by setting a result.
|
||||||
Logger.ResourceFilterShortCircuited(item.Filter);
|
Logger.ResourceFilterShortCircuited(item.Filter);
|
||||||
|
|
||||||
await InvokeResultAsync(_resourceExecutingContext.Result);
|
await InvokeResultAsync(_resourceExecutingContext.Result);
|
||||||
|
|
||||||
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
_resourceExecutedContext = new ResourceExecutedContext(_resourceExecutingContext, _filters)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
@ -346,5 +347,34 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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<JsonInputFormatter>();
|
||||||
|
context.OutputFormatters.RemoveType<JsonOutputFormatter>();
|
||||||
|
|
||||||
|
context.InputFormatters.Add(new JsonInputFormatter(_serializerSettings));
|
||||||
|
context.OutputFormatters.Add(new JsonOutputFormatter(_serializerSettings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue