Adding ClearMediaTypeMappingForFormat to FormatterMappings and other PR comments

This commit is contained in:
Mugdha Kulkarni 2015-01-22 17:05:15 -08:00
parent addd8dd5d2
commit 6a0f471a42
13 changed files with 166 additions and 34 deletions

View File

@ -1,4 +1,6 @@
using System;
// 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 System;
namespace MvcSample.Web
{

View File

@ -118,7 +118,7 @@ namespace Microsoft.AspNet.Mvc
CurrentCandidate = candidate
};
if (candidate.Constraints == null || candidate.Constraints.Count() == 0 ||
if (candidate.Constraints == null || candidate.Constraints.Count == 0 ||
candidate.Constraints.Any(constraint => constraint is IConsumesActionConstraint &&
constraint.Accept(tempContext)))
{

View File

@ -2,13 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNet.Mvc.Description;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers;
using System.Collections.Generic;
namespace Microsoft.AspNet.Mvc
{
@ -47,7 +47,7 @@ namespace Microsoft.AspNet.Mvc
filter.SetContentTypes(contentTypes);
}
if (contentTypes.Count() != 0)
if (contentTypes.Count != 0)
{
// If formatfilterContentType is not subset of any of the content types produced by
// IApiResponseMetadataProviders, return 404
@ -93,10 +93,11 @@ namespace Microsoft.AspNet.Mvc
}
/// <summary>
/// If the current request contains format value, returns true. It means the format filter is going to execute.
/// Returns <c>true</c> if the filter is active and will execute; otherwise, <c>false</c>. The filter is active
/// if the current request contains format value.
/// </summary>
/// <param name="context">The <see cref="FilterContext"/></param>
/// <returns>If the filter is active and will execute.</returns>
/// <returns><c>true</c> if the current request contains format value; otherwise, <c>false</c>.</returns>
public bool IsActive(FilterContext context)
{
var format = GetFormat(context);
@ -113,7 +114,12 @@ namespace Microsoft.AspNet.Mvc
format = context.HttpContext.Request.Query["format"];
}
return (string)format;
if (format != null)
{
return format.ToString();
}
return null;
}
private MediaTypeHeaderValue GetContentType(string format, FilterContext context)

View File

@ -1,23 +1,20 @@
// 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.Net.Http.Headers;
using System;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// A filter which produces a desired content type for the current request.
/// </summary>
/// <remarks>A FormatFilter decides what content type to use if a format value is present in the URL.
/// </remarks>
public interface IFormatFilter : IFilter
{
/// <summary>
/// Returns true if the filter is going to be executed for the current request.
/// Returns <c>true</c> if the filter will produce a content type for the current request, otherwise
/// <c>false</c>.
/// </summary>
/// <param name="context">The <see cref="FilterContext"/></param>
/// <returns>If the filter will execute</returns>
/// <returns><c>true</c> if the filter will produce a content type for the current request; otherwise,
/// <c>false</c>.</returns>
bool IsActive(FilterContext context);
}
}

View File

@ -22,8 +22,8 @@ namespace Microsoft.AspNet.Mvc
/// 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.
/// </summary>
/// <param name="format">format value</param>
/// <param name="contentType">The <see cref="MediaTypeHeaderValue"/> for the format value</param>
/// <param name="format">The format value.</param>
/// <param name="contentType">The <see cref="MediaTypeHeaderValue"/> for the format value.</param>
public void SetMediaTypeMappingForFormat([NotNull] string format, [NotNull] MediaTypeHeaderValue contentType)
{
ValidateContentType(contentType);
@ -34,8 +34,8 @@ namespace Microsoft.AspNet.Mvc
/// <summary>
/// Gets <see cref="MediaTypeHeaderValue"/> for the specified format.
/// </summary>
/// <param name="format">format value.</param>
/// <returns>The <see cref="MediaTypeHeaderValue"/> for input format</returns>
/// <param name="format">The format value.</param>
/// <returns>The <see cref="MediaTypeHeaderValue"/> for input format.</returns>
public MediaTypeHeaderValue GetMediaTypeMappingForFormat([NotNull] string format)
{
format = RemovePeriodIfPresent(format);
@ -46,9 +46,20 @@ namespace Microsoft.AspNet.Mvc
return value;
}
/// <summary>
/// Clears the <see cref="MediaTypeHeaderValue"/> mapping for the format.
/// </summary>
/// <param name="format">The format value.</param>
/// <returns><c>true</c> if the format is successfully found and cleared; otherwise, <c>false</c>.</returns>
public bool ClearMediaTypeMappingForFormat([NotNull] string format)
{
format = RemovePeriodIfPresent(format);
return _map.Remove(format);
}
private void ValidateContentType(MediaTypeHeaderValue contentType)
{
if(contentType.Type == "*" || contentType.SubType == "*")
if (contentType.Type == "*" || contentType.SubType == "*")
{
throw new ArgumentException(string.Format(Resources.FormatterMappings_NotValidMediaType, contentType));
}
@ -56,10 +67,20 @@ namespace Microsoft.AspNet.Mvc
private string RemovePeriodIfPresent(string format)
{
if (format == "")
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "format");
}
if (format.StartsWith("."))
{
format = format.Substring(1);
}
if (format == ".")
{
throw new ArgumentException(string.Format(Resources.Format_NotValid, format));
}
format = format.Substring(1);
}
return format;
}

View File

@ -562,11 +562,24 @@ namespace Microsoft.AspNet.Mvc.Core
get { return GetString("Common_ValueNotValidForProperty"); }
}
/// <summary>
/// The media type "{0}" is not valid. MediaTypes containing wildcards (*) are not allowed in formatter
/// mappings.
/// </summary>
internal static string FormatterMappings_NotValidMediaType
{
get { return GetString("FormatterMappings_NotValidMediaType"); }
}
/// <summary>
/// The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed
/// with a '.' character.
/// </summary>
internal static string Format_NotValid
{
get { return GetString("Format_NotValid"); }
}
/// <summary>
/// The value '{0}' is invalid.
/// </summary>

View File

@ -452,7 +452,10 @@
<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>
<value>The media type "{0}" is not valid. MediaTypes containing wildcards (*) are not allowed in formatter mappings.</value>
</data>
<data name="Format_NotValid" xml:space="preserve">
<value>The format provided is invalid '{0}'. A format must be a non-empty file-extension, optionally prefixed with a '.' character.</value>
</data>
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
<value>No URL for remote validation could be found.</value>

View File

@ -6,8 +6,8 @@ using System.Xml.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Razor;
using Microsoft.Framework.OptionsModel;
using Newtonsoft.Json.Linq;
using Microsoft.Net.Http.Headers;
using Newtonsoft.Json.Linq;
namespace Microsoft.AspNet.Mvc
{

View File

@ -118,7 +118,9 @@ namespace Microsoft.AspNet.Mvc
var resultExecutingContext = CreateResultExecutingContext(format, place);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place);
var options = resultExecutingContext.HttpContext.RequestServices.GetService<IOptions<MvcOptions>>();
options.Options.FormatterMappings.SetMediaTypeMappingForFormat(format, MediaTypeHeaderValue.Parse(contentType));
options.Options.FormatterMappings.SetMediaTypeMappingForFormat(
format,
MediaTypeHeaderValue.Parse(contentType));
var filter = new FormatFilterAttribute();
@ -194,7 +196,28 @@ namespace Microsoft.AspNet.Mvc
var produces = new ProducesAttribute("application/xml;version=1", 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"));
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_LessSpecificThan_Produces_Wildcard()
{
// Arrange
var produces = new ProducesAttribute("application/*", 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"));
var filter = new FormatFilterAttribute();
// Act
@ -211,7 +234,9 @@ namespace Microsoft.AspNet.Mvc
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"));
options.Options.FormatterMappings.SetMediaTypeMappingForFormat(
"xml",
MediaTypeHeaderValue.Parse("application/xml;version=1"));
var filter = new FormatFilterAttribute();
// Act

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Mvc
[InlineData("json", "application/json", "JSON")]
[InlineData(".foo", "text/foo", "Foo")]
[InlineData(".Json", "application/json", "json")]
[InlineData("FOo", "text/foo", "FOO")]
[InlineData("FOo", "text/foo", "FOO")]
public void FormatterMappings_SetFormatMapping_DiffSetGetFormat(string setFormat, string contentType, string getFormat)
{
// Arrange
@ -30,6 +30,38 @@ namespace Microsoft.AspNet.Mvc
Assert.Equal(mediaType, returnMediaType);
}
[Fact]
public void FormatterMappings_Invalid_Period()
{
// Arrange
var options = new FormatterMappings();
var format = ".";
var expected = string.Format(@"The format provided is invalid '{0}'. A format must be a non-empty file-" +
"extension, optionally prefixed with a '.' character.", format);
// Act and assert
var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat(
format,
MediaTypeHeaderValue.Parse("application/xml")));
Assert.Equal(expected, exception.Message);
}
[Fact]
public void FormatterMappings_SetFormatMapping_FormatEmpty()
{
// Arrange
var options = new FormatterMappings();
var format = "";
var expected = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: format";
// Act and assert
var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat(
format,
MediaTypeHeaderValue.Parse("application/xml")));
Assert.Equal(expected, exception.Message);
}
[Theory]
[InlineData("application/*")]
[InlineData("*/json")]
@ -38,11 +70,45 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var options = new FormatterMappings();
var expected = string.Format(@"The media type {0} is not valid. The media type containing ""<mediatype>/*"" are not supported.", format);
var expected = string.Format(@"The media type ""{0}"" is not valid. MediaTypes containing wildcards (*) " +
"are not allowed in formatter mappings.", format);
// Act and assert
var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat("star", MediaTypeHeaderValue.Parse(format)));
var exception = Assert.Throws<ArgumentException>(() => options.SetMediaTypeMappingForFormat(
"star",
MediaTypeHeaderValue.Parse(format)));
Assert.Equal(expected, exception.Message);
}
[Theory]
[InlineData(".xml", true)]
[InlineData("json", true)]
[InlineData(".foo", true)]
[InlineData(".Json", true)]
[InlineData("FOo", true)]
[InlineData("bar", true)]
[InlineData("baz", false)]
[InlineData(".baz", false)]
[InlineData("BAZ", false)]
public void FormatterMappings_ClearFormatMapping(string format, bool expected)
{
// Arrange
var options = new FormatterMappings();
var mediaType = MediaTypeHeaderValue.Parse("application/xml");
options.SetMediaTypeMappingForFormat("xml", mediaType);
mediaType = MediaTypeHeaderValue.Parse("application/json");
options.SetMediaTypeMappingForFormat("json", mediaType);
mediaType = MediaTypeHeaderValue.Parse("application/foo");
options.SetMediaTypeMappingForFormat("foo", mediaType);
mediaType = MediaTypeHeaderValue.Parse("application/bar");
options.SetMediaTypeMappingForFormat("bar", mediaType);
// Act
var cleared = options.ClearMediaTypeMappingForFormat(format);
// Assert
Assert.Equal(expected, cleared);
Assert.Null(options.GetMediaTypeMappingForFormat(format));
}
}
}

View File

@ -1,10 +1,9 @@
// 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 ConnegWebSite;
using Microsoft.AspNet.Mvc;
namespace ConnegWebsite
namespace ConnegWebSite
{
[Produces("application/FormatFilterController")]
public class FormatFilterController : Controller

View File

@ -11,7 +11,6 @@ namespace FormatFilterWebSite
[Produces("application/ProducesMethod")]
public string ReturnClassName()
{
// should be written using the content defined at base class's action.
return "ProducesOverrideController";
}
}

View File

@ -27,6 +27,8 @@ namespace FormatFilterWebSite
var actionReturn = context.Object as Product;
if (actionReturn != null)
{
var response = context.ActionContext.HttpContext.Response;
context.SelectedContentType = contentType;
return true;
}
}
@ -35,8 +37,7 @@ namespace FormatFilterWebSite
public override async Task WriteResponseBodyAsync(OutputFormatterContext context)
{
var response = context.ActionContext.HttpContext.Response;
response.ContentType = ContentType + ";charset=utf-8";
var response = context.ActionContext.HttpContext.Response;
await response.WriteAsync(context.Object.ToString());
}
}