Changing produces and format filter interaction

This commit is contained in:
Mugdha Kulkarni 2015-01-21 22:55:54 -08:00
parent 7ee80020e1
commit addd8dd5d2
22 changed files with 163 additions and 158 deletions

View File

@ -5,7 +5,6 @@ using Microsoft.AspNet.Mvc;
namespace MvcSample.Web.Controllers namespace MvcSample.Web.Controllers
{ {
[FormatFilter]
public class FormatFilterController : Controller public class FormatFilterController : Controller
{ {
public Product GetProduct(int id) public Product GetProduct(int id)

View File

@ -63,9 +63,8 @@ namespace MvcSample.Web
services.Configure<MvcOptions>(options => services.Configure<MvcOptions>(options =>
{ {
options.Filters.Add(typeof(PassThroughAttribute), order: 17); options.Filters.Add(typeof(PassThroughAttribute), order: 17);
options.AddXmlDataContractSerializerFormatter(); options.AddXmlDataContractSerializerFormatter();
var formatFilter = new FormatFilterAttribute(); options.Filters.Add(new FormatFilterAttribute());
options.Filters.Add(formatFilter);
}); });
services.Configure<RazorViewEngineOptions>(options => services.Configure<RazorViewEngineOptions>(options =>
{ {
@ -108,9 +107,7 @@ namespace MvcSample.Web
{ {
options.Filters.Add(typeof(PassThroughAttribute), order: 17); options.Filters.Add(typeof(PassThroughAttribute), order: 17);
options.AddXmlDataContractSerializerFormatter(); options.AddXmlDataContractSerializerFormatter();
options.Filters.Add(new FormatFilterAttribute());
var formatFilter = new FormatFilterAttribute();
options.Filters.Add(formatFilter);
}); });
}); });
} }

View File

@ -13,19 +13,18 @@ using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
/// <summary> /// <summary>
/// This will look at the format parameter if present in the route data or query data and sets the content type in /// A filter which will use the format value in the route data or query string to set the content type on an
/// ObjectResult corresponding to the format value. /// <see cref="ObjectResult" /> returned from an action.
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class FormatFilterAttribute : Attribute, IFormatFilter, IResourceFilter, IResultFilter public class FormatFilterAttribute : Attribute, IFormatFilter, IResourceFilter, IResultFilter
{ {
/// <summary> /// <summary>
/// As a resourceFilter, this filter looks at the request and rejects it /// As a <see cref="IResourceFilter"/>, this filter looks at the request and rejects it before going ahead if
/// before going ahead if
/// 1. The format in the request doesnt match any format in the map. /// 1. The format in the request doesnt match any format in the map.
/// 2. If there is a conflicting producesFilter. /// 2. If there is a conflicting producesFilter.
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context">The <see cref="ResourceExecutingContext"/>.</param>
public void OnResourceExecuting([NotNull] ResourceExecutingContext context) public void OnResourceExecuting([NotNull] ResourceExecutingContext context)
{ {
var format = GetFormat(context); var format = GetFormat(context);
@ -40,21 +39,23 @@ namespace Microsoft.AspNet.Mvc
} }
else else
{ {
var responseTypeFilters = context.Filters.OfType<IApiResponseMetadataProvider>(); var responseTypeFilters = context.Filters.OfType<IApiResponseMetadataProvider>();
if (responseTypeFilters.Count() != 0) var contentTypes = new List<MediaTypeHeaderValue>();
foreach (var filter in responseTypeFilters)
{ {
var contentTypes = new List<MediaTypeHeaderValue>(); filter.SetContentTypes(contentTypes);
}
foreach (var filter in responseTypeFilters) if (contentTypes.Count() != 0)
{ {
filter.SetContentTypes(contentTypes); // If formatfilterContentType is not subset of any of the content types produced by
} // IApiResponseMetadataProviders, return 404
if (!contentTypes.Any(c => formatContentType.IsSubsetOf(c)))
if (!contentTypes.Any(c => c.IsSubsetOf(formatContentType)))
{ {
context.Result = new HttpNotFoundResult(); context.Result = new HttpNotFoundResult();
} }
} }
} }
} }
} }
@ -66,19 +67,19 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// This method looks at the format that request has and sets it in the context /// Sets a Content Type on an <see cref="ObjectResult" /> using a format value from the request.
/// </summary> /// </summary>
/// <param name="context">The <see cref="ResultExecutingContext"/>.</param>
public void OnResultExecuting([NotNull] ResultExecutingContext context) public void OnResultExecuting([NotNull] ResultExecutingContext context)
{ {
var format = GetFormat(context); var format = GetFormat(context);
if (format != null) if (!string.IsNullOrEmpty(format))
{ {
var contentType = GetContentType(format, context);
Debug.Assert(contentType != null);
var objectResult = context.Result as ObjectResult; var objectResult = context.Result as ObjectResult;
if (objectResult != null) if (objectResult != null)
{ {
var contentType = GetContentType(format, context);
Debug.Assert(contentType != null);
objectResult.ContentTypes.Clear(); objectResult.ContentTypes.Clear();
objectResult.ContentTypes.Add(contentType); objectResult.ContentTypes.Add(contentType);
} }
@ -92,20 +93,15 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// Looks at the current request for the format parameter. If it contains format, it returns the content type /// If the current request contains format value, returns true. It means the format filter is going to execute.
/// for it.
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context">The <see cref="FilterContext"/></param>
/// <returns></returns> /// <returns>If the filter is active and will execute.</returns>
public MediaTypeHeaderValue GetContentTypeForCurrentRequest(FilterContext context) public bool IsActive(FilterContext context)
{ {
var format = GetFormat(context); var format = GetFormat(context);
if (format != null && !string.IsNullOrEmpty(format.ToString()))
{
return GetContentType(format, context);
}
return null; return !string.IsNullOrEmpty(format);
} }
private string GetFormat(FilterContext context) private string GetFormat(FilterContext context)
@ -114,25 +110,17 @@ namespace Microsoft.AspNet.Mvc
if (!context.RouteData.Values.TryGetValue("format", out format)) if (!context.RouteData.Values.TryGetValue("format", out format))
{ {
if (context.HttpContext.Request.Query.ContainsKey("format")) format = context.HttpContext.Request.Query["format"];
{
format = context.HttpContext.Request.Query.Get("format");
return format.ToString();
}
}
else
{
return format.ToString();
} }
return null; return (string)format;
} }
private MediaTypeHeaderValue GetContentType(object format, FilterContext context) private MediaTypeHeaderValue GetContentType(string format, FilterContext context)
{ {
Debug.Assert(format != null);
var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>(); var options = context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>();
var contentType = options.Options.FormatterMappings.GetMediaTypeForFormat(format.ToString()); var contentType = options.Options.FormatterMappings.GetMediaTypeMappingForFormat(format);
return contentType; return contentType;
} }
} }

View File

@ -7,14 +7,17 @@ using System;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
/// <summary> /// <summary>
/// Implement this interface if you want to have your own implementation of FormatFilter. A FormatFilter decides /// A filter which produces a desired content type for the current request.
/// what content type to use if the format is present in the Url.
/// </summary> /// </summary>
/// <remarks>A FormatFilter decides what content type to use if a format value is present in the URL.
/// </remarks>
public interface IFormatFilter : IFilter public interface IFormatFilter : IFilter
{ {
/// <summary> /// <summary>
/// Get the <see cref="MediaTypeHeaderValue"/> registered fot the format in the request. /// Returns true if the filter is going to be executed for the current request.
/// </summary> /// </summary>
MediaTypeHeaderValue GetContentTypeForCurrentRequest(FilterContext context); /// <param name="context">The <see cref="FilterContext"/></param>
/// <returns>If the filter will execute</returns>
bool IsActive(FilterContext context);
} }
} }

View File

@ -34,10 +34,9 @@ namespace Microsoft.AspNet.Mvc
if (objectResult != null) if (objectResult != null)
{ {
// Check if FormatFilter has already set the content type // Check if there are any IFormatFilter in the pipeline, and if any of them is active. If there is one,
// If it has, dont override it // do not override the content type value.
var formatFilter = context.Filters.OfType<IFormatFilter>().LastOrDefault(); if (context.Filters.OfType<IFormatFilter>().All(f => !f.IsActive(context)))
if (formatFilter == null || formatFilter.GetContentTypeForCurrentRequest(context) == null)
{ {
SetContentTypes(objectResult.ContentTypes); SetContentTypes(objectResult.ContentTypes);
} }

View File

@ -11,7 +11,7 @@ using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
/// <summary> /// <summary>
/// Used to specify mapping between the Url Format and corresponding <see cref="MediaTypeHeaderValue"/>. /// Used to specify mapping between the URL Format and corresponding <see cref="MediaTypeHeaderValue"/>.
/// </summary> /// </summary>
public class FormatterMappings public class FormatterMappings
{ {
@ -19,21 +19,14 @@ namespace Microsoft.AspNet.Mvc
new Dictionary<string, MediaTypeHeaderValue>(StringComparer.OrdinalIgnoreCase); new Dictionary<string, MediaTypeHeaderValue>(StringComparer.OrdinalIgnoreCase);
/// <summary> /// <summary>
/// This will set mapping for the format to specified <see cref="MediaTypeHeaderValue"/>. /// Sets mapping for the format to specified <see cref="MediaTypeHeaderValue"/>.
/// If the format already exists, the <see cref="MediaTypeHeaderValue"/> will be overwritten with the new value. /// If the format already exists, the <see cref="MediaTypeHeaderValue"/> will be overwritten with the new value.
/// </summary> /// </summary>
/// <param name="format">format value</param>
/// <param name="contentType">The <see cref="MediaTypeHeaderValue"/> for the format value</param>
public void SetMediaTypeMappingForFormat([NotNull] string format, [NotNull] MediaTypeHeaderValue contentType) public void SetMediaTypeMappingForFormat([NotNull] string format, [NotNull] MediaTypeHeaderValue contentType)
{ {
if (string.IsNullOrEmpty(format)) ValidateContentType(contentType);
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "format");
}
if (contentType == null)
{
throw new ArgumentException((Resources.ArgumentCannotBeNullOrEmpty), "contentType");
}
format = RemovePeriodIfPresent(format); format = RemovePeriodIfPresent(format);
_map[format] = contentType; _map[format] = contentType;
} }
@ -41,14 +34,26 @@ namespace Microsoft.AspNet.Mvc
/// <summary> /// <summary>
/// Gets <see cref="MediaTypeHeaderValue"/> for the specified format. /// Gets <see cref="MediaTypeHeaderValue"/> for the specified format.
/// </summary> /// </summary>
public MediaTypeHeaderValue GetMediaTypeForFormat(string format) /// <param name="format">format value.</param>
/// <returns>The <see cref="MediaTypeHeaderValue"/> for input format</returns>
public MediaTypeHeaderValue GetMediaTypeMappingForFormat([NotNull] string format)
{ {
format = RemovePeriodIfPresent(format); format = RemovePeriodIfPresent(format);
MediaTypeHeaderValue value = null; MediaTypeHeaderValue value = null;
_map.TryGetValue(format, out value); _map.TryGetValue(format, out value);
return value; return value;
} }
private void ValidateContentType(MediaTypeHeaderValue contentType)
{
if(contentType.Type == "*" || contentType.SubType == "*")
{
throw new ArgumentException(string.Format(Resources.FormatterMappings_NotValidMediaType, contentType));
}
}
private string RemovePeriodIfPresent(string format) private string RemovePeriodIfPresent(string format)
{ {
if (format.StartsWith(".")) if (format.StartsWith("."))

View File

@ -55,7 +55,7 @@ namespace Microsoft.AspNet.Mvc
} }
/// <summary> /// <summary>
/// Used to specify mapping between the Url Format and corresponding <see cref="MediaTypeHeaderValue"/>. /// Used to specify mapping between the URL Format and corresponding <see cref="MediaTypeHeaderValue"/>.
/// </summary> /// </summary>
public FormatterMappings FormatterMappings { get; } public FormatterMappings FormatterMappings { get; }

View File

@ -562,6 +562,11 @@ namespace Microsoft.AspNet.Mvc.Core
get { return GetString("Common_ValueNotValidForProperty"); } get { return GetString("Common_ValueNotValidForProperty"); }
} }
internal static string FormatterMappings_NotValidMediaType
{
get { return GetString("FormatterMappings_NotValidMediaType"); }
}
/// <summary> /// <summary>
/// The value '{0}' is invalid. /// The value '{0}' is invalid.
/// </summary> /// </summary>

View File

@ -450,6 +450,9 @@
</data> </data>
<data name="ApiExplorer_UnsupportedAction" xml:space="preserve"> <data name="ApiExplorer_UnsupportedAction" xml:space="preserve">
<value>The action '{0}' has ApiExplorer enabled, but is using conventional routing. Only actions which use attribute routing support ApiExplorer.</value> <value>The action '{0}' has ApiExplorer enabled, but is using conventional routing. Only actions which use attribute routing support ApiExplorer.</value>
</data>
<data name="FormatterMappings_NotValidMediaType" xml:space="preserve">
<value>The media type {0} is not valid. The media type containing "&lt;mediatype&gt;/*" are not supported.</value>
</data> </data>
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve"> <data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
<value>No URL for remote validation could be found.</value> <value>No URL for remote validation could be found.</value>

View File

@ -171,8 +171,8 @@ namespace Microsoft.AspNet.Mvc
[InlineData("json", FormatSource.RouteData, "application/json")] [InlineData("json", FormatSource.RouteData, "application/json")]
[InlineData("json", FormatSource.QueryData, "application/json")] [InlineData("json", FormatSource.QueryData, "application/json")]
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Matching( public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Matching(
string format, string format,
FormatSource place, FormatSource place,
string contentType) string contentType)
{ {
// Arrange // Arrange
@ -188,23 +188,38 @@ namespace Microsoft.AspNet.Mvc
} }
[Fact] [Fact]
public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_WildCardMatching() public void FormatFilter_LessSpecificThan_Produces()
{ {
// Arrange // Arrange
var produces = new ProducesAttribute( var produces = new ProducesAttribute("application/xml;version=1", new string [] { });
"application/baz", var context = CreateResourceExecutingContext(new IFilter[] { produces }, "xml", FormatSource.RouteData);
new string[] { "application/foo", "text/bar" });
var context = CreateResourceExecutingContext(new IFilter[] { produces }, "star", FormatSource.RouteData);
var options = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>(); var options = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
options.Options.FormatterMappings.SetMediaTypeMappingForFormat("star", MediaTypeHeaderValue.Parse("application/*")); options.Options.FormatterMappings.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("application/xml"));
var filter = new FormatFilterAttribute();
// Act
filter.OnResourceExecuting(context);
// Assert
Assert.Null(context.Result);
}
[Fact]
public void FormatFilter_MoreSpecificThan_Produces()
{
// Arrange
var produces = new ProducesAttribute("application/xml", new string[] { });
var context = CreateResourceExecutingContext(new IFilter[] { produces }, "xml", FormatSource.RouteData);
var options = context.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
options.Options.FormatterMappings.SetMediaTypeMappingForFormat("xml", MediaTypeHeaderValue.Parse("application/xml;version=1"));
var filter = new FormatFilterAttribute(); var filter = new FormatFilterAttribute();
// Act // Act
filter.OnResourceExecuting(context); filter.OnResourceExecuting(context);
// Assert // Assert
Assert.Null(context.Result); var actionResult = context.Result;
Assert.IsType<HttpNotFoundResult>(actionResult);
} }
[Theory] [Theory]
@ -241,7 +256,7 @@ namespace Microsoft.AspNet.Mvc
var resourceExecutingContext = CreateResourceExecutingContext( var resourceExecutingContext = CreateResourceExecutingContext(
new IFilter[] { }, new IFilter[] { },
format, format,
FormatSource.RouteData); place);
var filter = new FormatFilterAttribute(); var filter = new FormatFilterAttribute();
// Act // Act
@ -252,31 +267,24 @@ namespace Microsoft.AspNet.Mvc
} }
[Theory] [Theory]
[InlineData("json", FormatSource.RouteData, "application/json")] [InlineData("json", FormatSource.RouteData, true)]
[InlineData("json", FormatSource.QueryData, "application/json")] [InlineData("json", FormatSource.QueryData, true )]
[InlineData("", FormatSource.RouteAndQueryData, null)] [InlineData("", FormatSource.RouteAndQueryData, false)]
[InlineData(null, FormatSource.RouteAndQueryData, null)] [InlineData(null, FormatSource.RouteAndQueryData, false)]
public void FormatFilter_GetContentTypeForRequest( public void FormatFilter_IsActive(
string format, string format,
FormatSource place, FormatSource place,
string contentType) bool expected)
{ {
// Arrange // Arrange
var resourceExecutingContext = CreateResourceExecutingContext( var resultExecutingContext = CreateResultExecutingContext(format, place);
new IFilter[] { },
format,
FormatSource.RouteData);
var filter = new FormatFilterAttribute(); var filter = new FormatFilterAttribute();
var returnContentType = filter.GetContentTypeForCurrentRequest(resourceExecutingContext);
// Act
var isActive = filter.IsActive(resultExecutingContext);
MediaTypeHeaderValue mediaType = null; // Assert
if (returnContentType != null) Assert.Equal(expected, isActive);
{
mediaType = MediaTypeHeaderValue.Parse("application/json");
}
Assert.Equal(mediaType, returnContentType);
} }
private static ResourceExecutingContext CreateResourceExecutingContext( private static ResourceExecutingContext CreateResourceExecutingContext(
@ -332,7 +340,7 @@ namespace Microsoft.AspNet.Mvc
if (place == FormatSource.QueryData || place == FormatSource.RouteAndQueryData) if (place == FormatSource.QueryData || place == FormatSource.RouteAndQueryData)
{ {
httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true); httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true);
httpContext.Setup(c => c.Request.Query.Get("format")).Returns(format); httpContext.Setup(c => c.Request.Query["format"]).Returns(format);
} }
else if (place == null && format == null) else if (place == null && format == null)
{ {

View File

@ -39,16 +39,38 @@ namespace Microsoft.AspNet.Mvc.Test
} }
[Fact] [Fact]
public async Task ProducesContentAttribute_FormatFilterAttribute() public async Task ProducesContentAttribute_FormatFilterAttribute_NotActive()
{ {
// Arrange // Arrange
var mediaType1 = MediaTypeHeaderValue.Parse("application/xml");
var mediaType2 = MediaTypeHeaderValue.Parse("application/json");
var producesContentAttribute = new ProducesAttribute("application/xml"); var producesContentAttribute = new ProducesAttribute("application/xml");
var formatFilter = new Mock<IFormatFilter>(); var formatFilter = new Mock<IFormatFilter>();
formatFilter.Setup(f => f.GetContentTypeForCurrentRequest(It.IsAny<FilterContext>())) formatFilter.Setup(f => f.IsActive(It.IsAny<FilterContext>()))
.Returns(mediaType2); .Returns(false);
var filters = new IFilter[] { producesContentAttribute, formatFilter.Object };
var resultExecutingContext = CreateResultExecutingContext(filters);
var next = new ResultExecutionDelegate(
() => Task.FromResult(CreateResultExecutedContext(resultExecutingContext)));
// Act
await producesContentAttribute.OnResultExecutionAsync(resultExecutingContext, next);
// Assert
var objectResult = Assert.IsType<ObjectResult>(resultExecutingContext.Result);
Assert.Equal(1, objectResult.ContentTypes.Count);
}
[Fact]
public async Task ProducesContentAttribute_FormatFilterAttribute_Active()
{
// Arrange
var producesContentAttribute = new ProducesAttribute("application/xml");
var formatFilter = new Mock<IFormatFilter>();
formatFilter.Setup(f => f.IsActive(It.IsAny<FilterContext>()))
.Returns(true);
var filters = new IFilter[] { producesContentAttribute, formatFilter.Object }; var filters = new IFilter[] { producesContentAttribute, formatFilter.Object };
var resultExecutingContext = CreateResultExecutingContext(filters); var resultExecutingContext = CreateResultExecutingContext(filters);
@ -64,7 +86,6 @@ namespace Microsoft.AspNet.Mvc.Test
Assert.Equal(0, objectResult.ContentTypes.Count); Assert.Equal(0, objectResult.ContentTypes.Count);
} }
[Theory] [Theory]
[InlineData("", "")] [InlineData("", "")]
[InlineData("application/xml,, application/json", "")] [InlineData("application/xml,, application/json", "")]

View File

@ -24,31 +24,25 @@ namespace Microsoft.AspNet.Mvc
options.SetMediaTypeMappingForFormat(setFormat, mediaType); options.SetMediaTypeMappingForFormat(setFormat, mediaType);
// Act // Act
var returnMediaType = options.GetMediaTypeForFormat(getFormat); var returnMediaType = options.GetMediaTypeMappingForFormat(getFormat);
// Assert // Assert
Assert.Equal(mediaType, returnMediaType); Assert.Equal(mediaType, returnMediaType);
} }
[Theory] [Theory]
[InlineData("xml", null)] [InlineData("application/*")]
[InlineData(".json", null)] [InlineData("*/json")]
[InlineData(null, "application/json")] [InlineData("*/*")]
[InlineData("", "text/foo")] public void FormatterMappings_Wildcardformat(string format)
public void FormatterMappings_SetFormatMapping_Invalid(string format, string contentType)
{ {
// Arrange // Arrange
MediaTypeHeaderValue mediaType = null; var options = new FormatterMappings();
if (!string.IsNullOrEmpty(contentType)) var expected = string.Format(@"The media type {0} is not valid. The media type containing ""<mediatype>/*"" are not supported.", format);
{
mediaType = MediaTypeHeaderValue.Parse(contentType);
}
var options = new FormatterMappings(); // Act and assert
var expectedError = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: format"; var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat("star", MediaTypeHeaderValue.Parse(format)));
Assert.Equal(expected, exception.Message);
// Act and Assert
Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat(format, mediaType));
} }
} }
} }

View File

@ -141,10 +141,11 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var client = server.CreateClient(); var client = server.CreateClient();
// Act // Act
var response = await client.GetAsync("http://localhost/ProducesDerived/ReturnClassName.json"); var response = await client.GetAsync("http://localhost/ProducesOverride/ReturnClassName");
// Assert // Assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode); Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(@"ProducesOverrideController", await response.Content.ReadAsStringAsync());
} }
} }
} }

View File

@ -20,4 +20,9 @@
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project> </Project>

View File

@ -17,6 +17,7 @@
"ErrorPageMiddlewareWebSite": "1.0.0", "ErrorPageMiddlewareWebSite": "1.0.0",
"FilesWebSite": "1.0.0", "FilesWebSite": "1.0.0",
"FiltersWebSite": "1.0.0", "FiltersWebSite": "1.0.0",
"FormatFilterWebSite": "1.0.0-*",
"FormatterWebSite": "1.0.0", "FormatterWebSite": "1.0.0",
"InlineConstraintsWebSite": "1.0.0", "InlineConstraintsWebSite": "1.0.0",
"LoggingWebSite": "1.0.0", "LoggingWebSite": "1.0.0",
@ -44,8 +45,7 @@
"MvcTagHelpersWebSite": "1.0.0", "MvcTagHelpersWebSite": "1.0.0",
"Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*",
"xunit.runner.kre": "1.0.0-*", "xunit.runner.kre": "1.0.0-*",
"Microsoft.AspNet.WebUtilities": "1.0.0-*", "Microsoft.AspNet.WebUtilities": "1.0.0-*"
"FormatFilterWebSite": "1.0.0-*"
}, },
"commands": { "commands": {
"test": "xunit.runner.kre" "test": "xunit.runner.kre"

View File

@ -37,6 +37,6 @@ namespace ConnegWebSite
public virtual string ReturnClassNameContentTypeOnDerivedAction() public virtual string ReturnClassNameContentTypeOnDerivedAction()
{ {
return "ProducesContentBaseController"; return "ProducesContentBaseController";
} }
} }
} }

View File

@ -39,6 +39,6 @@ namespace ConnegWebSite
{ {
// should be written using the content defined at derived class's class. // should be written using the content defined at derived class's class.
return "ProducesContentOnClassController"; return "ProducesContentOnClassController";
} }
} }
} }

View File

@ -31,7 +31,7 @@ namespace ConnegWebSite
app.UseMvc(routes => app.UseMvc(routes =>
{ {
routes.MapRoute("ActionAsMethod", "{controller}/{action}", routes.MapRoute("ActionAsMethod", "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }); defaults: new { controller = "Home", action = "Index" });
}); });
} }
} }

View File

@ -15,9 +15,4 @@
<DevelopmentServerPort>49641</DevelopmentServerPort> <DevelopmentServerPort>49641</DevelopmentServerPort>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project> </Project>

View File

@ -5,7 +5,6 @@ using System.Security.Claims;
using Microsoft.AspNet.Builder; using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Security; using Microsoft.AspNet.Security;
using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
namespace FiltersWebSite namespace FiltersWebSite

View File

@ -1,17 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Mvc;
namespace FormatFilterWebSite
{
public class ProducesBaseController : Controller
{
[Produces("application/custom_ProducesBaseController_Action")]
public virtual string ReturnClassName()
{
// Should be written using the action's content type. Overriding the one at the class.
return "ProducesBaseController";
}
}
}

View File

@ -5,11 +5,11 @@ using Microsoft.AspNet.Mvc;
namespace FormatFilterWebSite namespace FormatFilterWebSite
{ {
[Produces("application/custom_ProducesOverrideController")] [Produces("application/custom_ProducesController")]
public class ProducesOverrideController : ProducesBaseController public class ProducesOverrideController
{ {
[FormatFilter] [Produces("application/ProducesMethod")]
public override string ReturnClassName() public string ReturnClassName()
{ {
// should be written using the content defined at base class's action. // should be written using the content defined at base class's action.
return "ProducesOverrideController"; return "ProducesOverrideController";