Adding Input Formatters to MVC Options and using HeaderValueAbstractions
This commit is contained in:
parent
944fafb589
commit
d97a427b3d
|
|
@ -0,0 +1,39 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.OptionDescriptors
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DefaultInputFormattersProvider
|
||||
: OptionDescriptorBasedProvider<IInputFormatter>, IInputFormattersProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the DefaultInputFormattersProvider class.
|
||||
/// </summary>
|
||||
/// <param name="options">An accessor to the <see cref="MvcOptions"/> configured for this application.</param>
|
||||
/// <param name="typeActivator">An <see cref="ITypeActivator"/> instance used to instantiate types.</param>
|
||||
/// <param name="serviceProvider">A <see cref="IServiceProvider"/> instance that retrieves services from the
|
||||
/// service collection.</param>
|
||||
public DefaultInputFormattersProvider(IOptionsAccessor<MvcOptions> optionsAccessor,
|
||||
ITypeActivator typeActivator,
|
||||
IServiceProvider serviceProvider)
|
||||
: base(optionsAccessor.Options.InputFormatters, typeActivator, serviceProvider)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<IInputFormatter> InputFormatters
|
||||
{
|
||||
get
|
||||
{
|
||||
return Options;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an activated collection of <see cref="IInputFormatter"/> instances.
|
||||
/// </summary>
|
||||
public interface IInputFormattersProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a collection of activated InputFormatter instances.
|
||||
/// </summary>
|
||||
IReadOnlyList<IInputFormatter> InputFormatters { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class TempInputFormatterProvider : IInputFormatterProvider
|
||||
{
|
||||
private IInputFormattersProvider _defaultFormattersProvider;
|
||||
|
||||
public TempInputFormatterProvider([NotNull] IInputFormattersProvider formattersProvider)
|
||||
{
|
||||
_defaultFormattersProvider = formattersProvider;
|
||||
}
|
||||
|
||||
public IInputFormatter GetInputFormatter(InputFormatterProviderContext context)
|
||||
{
|
||||
var request = context.HttpContext.Request;
|
||||
MediaTypeHeaderValue contentType;
|
||||
if (!MediaTypeHeaderValue.TryParse(request.ContentType, out contentType))
|
||||
{
|
||||
// TODO: https://github.com/aspnet/Mvc/issues/458
|
||||
throw new InvalidOperationException("400: Bad Request");
|
||||
}
|
||||
|
||||
var formatters = _defaultFormattersProvider.InputFormatters;
|
||||
foreach (var formatter in formatters)
|
||||
{
|
||||
var formatterMatched = formatter.SupportedMediaTypes
|
||||
.Any(supportedMediaType =>
|
||||
supportedMediaType.IsSubsetOf(contentType));
|
||||
if (formatterMatched)
|
||||
{
|
||||
return formatter;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: https://github.com/aspnet/Mvc/issues/458
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
||||
"415: Unsupported content type {0}",
|
||||
contentType.RawValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||
using Microsoft.AspNet.Mvc.ReflectedModelBuilder;
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ namespace Microsoft.AspNet.Mvc
|
|||
ViewEngines = new List<ViewEngineDescriptor>();
|
||||
ValueProviderFactories = new List<ValueProviderFactoryDescriptor>();
|
||||
OutputFormatters = new List<OutputFormatterDescriptor>();
|
||||
InputFormatters = new List<InputFormatterDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -51,6 +53,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
|
||||
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
|
||||
|
||||
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides programmatic configuration for the default <see cref="Rendering.IViewEngine" />.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// 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;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.OptionDescriptors
|
||||
{
|
||||
/// <summary>
|
||||
/// Encapsulates information that describes an <see cref="IInputFormatter"/>.
|
||||
/// </summary>
|
||||
public class InputFormatterDescriptor : OptionDescriptor<IInputFormatter>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputFormatterDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">A <see cref="IOutputFormatter"/> type that the descriptor represents.
|
||||
/// </param>
|
||||
public InputFormatterDescriptor([NotNull] Type type)
|
||||
: base(type)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="InputFormatterDescriptor"/>.
|
||||
/// </summary>
|
||||
/// <param name="inputFormatter">An instance of <see cref="IInputFormatter"/>
|
||||
/// that the descriptor represents.</param>
|
||||
public InputFormatterDescriptor([NotNull] IInputFormatter inputFormatter)
|
||||
: base(inputFormatter)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for adding Input formatters to a collection.
|
||||
/// </summary>
|
||||
public static class InputFormatterDescriptorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a type representing a <see cref="IInputFormatter"/> to a descriptor collection.
|
||||
/// </summary>
|
||||
/// <param name="descriptors">A list of InputFormatterDescriptors</param>
|
||||
/// <param name="inputFormatterType">Type representing an <see cref="IInputFormatter"/>.</param>
|
||||
/// <returns>InputFormatterDescriptor representing the added instance.</returns>
|
||||
public static InputFormatterDescriptor Add([NotNull] this IList<InputFormatterDescriptor> descriptors,
|
||||
[NotNull] Type inputFormatterType)
|
||||
{
|
||||
var descriptor = new InputFormatterDescriptor(inputFormatterType);
|
||||
descriptors.Add(descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a type representing a <see cref="IInputFormatter"/> to a descriptor collection.
|
||||
/// </summary>
|
||||
/// <param name="descriptors">A list of InputFormatterDescriptors</param>
|
||||
/// <param name="inputFormatterType">Type representing an <see cref="IInputFormatter"/>.</param>
|
||||
/// <returns>InputFormatterDescriptor representing the inserted instance.</returns>
|
||||
public static InputFormatterDescriptor Insert([NotNull] this IList<InputFormatterDescriptor> descriptors,
|
||||
int index,
|
||||
[NotNull] Type inputFormatterType)
|
||||
{
|
||||
if (index < 0 || index > descriptors.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
|
||||
var descriptor = new InputFormatterDescriptor(inputFormatterType);
|
||||
descriptors.Insert(index, descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an <see cref="IInputFormatter"/> to a descriptor collection.
|
||||
/// </summary>
|
||||
/// <param name="descriptors">A list of InputFormatterDescriptors</param>
|
||||
/// <param name="inputFormatter">An <see cref="IInputFormatter"/> instance.</param>
|
||||
/// <returns>InputFormatterDescriptor representing the added instance.</returns>
|
||||
public static InputFormatterDescriptor Add([NotNull] this IList<InputFormatterDescriptor> descriptors,
|
||||
[NotNull] IInputFormatter inputFormatter)
|
||||
{
|
||||
var descriptor = new InputFormatterDescriptor(inputFormatter);
|
||||
descriptors.Add(descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Insert an <see cref="IInputFormatter"/> to a descriptor collection.
|
||||
/// </summary>
|
||||
/// <param name="descriptors">A list of InputFormatterDescriptors</param>
|
||||
/// <param name="inputFormatter">An <see cref="IInputFormatter"/> instance.</param>
|
||||
/// <returns>InputFormatterDescriptor representing the added instance.</returns>
|
||||
public static InputFormatterDescriptor Insert([NotNull] this IList<InputFormatterDescriptor> descriptors,
|
||||
int index,
|
||||
[NotNull] IInputFormatter inputFormatter)
|
||||
{
|
||||
if (index < 0 || index > descriptors.Count)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("index");
|
||||
}
|
||||
|
||||
var descriptor = new InputFormatterDescriptor(inputFormatter);
|
||||
descriptors.Insert(index, descriptor);
|
||||
return descriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,8 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
|
|||
|
||||
public static new MediaTypeWithQualityHeaderValue Parse(string input)
|
||||
{
|
||||
var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(input);
|
||||
if (mediaTypeHeaderValue == null)
|
||||
MediaTypeHeaderValue mediaTypeHeaderValue;
|
||||
if (!MediaTypeHeaderValue.TryParse(input, out mediaTypeHeaderValue))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -34,35 +35,5 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return XmlDictionaryReaderQuotas.Max;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Internal because ContentTypeHeaderValue is internal.
|
||||
internal static Encoding SelectCharacterEncoding(IList<Encoding> supportedEncodings,
|
||||
ContentTypeHeaderValue contentType, Type callerType)
|
||||
{
|
||||
if (contentType != null)
|
||||
{
|
||||
// Find encoding based on content type charset parameter
|
||||
var charset = contentType.CharSet;
|
||||
if (!string.IsNullOrWhiteSpace(contentType.CharSet))
|
||||
{
|
||||
for (var i = 0; i < supportedEncodings.Count; i++)
|
||||
{
|
||||
var supportedEncoding = supportedEncodings[i];
|
||||
if (string.Equals(charset, supportedEncoding.WebName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return supportedEncoding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (supportedEncodings.Count > 0)
|
||||
{
|
||||
return supportedEncodings[0];
|
||||
}
|
||||
|
||||
// No supported encoding was found so there is no way for us to start reading.
|
||||
throw new InvalidOperationException(Resources.FormatMediaTypeFormatterNoEncoding(callerType.FullName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// <summary>
|
||||
/// Gets the mutable collection of media types supported by this <see cref="JsonInputFormatter"/> instance.
|
||||
/// </summary>
|
||||
IList<string> SupportedMediaTypes { get; }
|
||||
IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mutable collection of character encodings supported by this <see cref="JsonInputFormatter"/>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -15,23 +16,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
public class JsonInputFormatter : IInputFormatter
|
||||
{
|
||||
private const int DefaultMaxDepth = 32;
|
||||
private readonly List<Encoding> _supportedEncodings;
|
||||
private readonly List<string> _supportedMediaTypes;
|
||||
private JsonSerializerSettings _jsonSerializerSettings;
|
||||
|
||||
public JsonInputFormatter()
|
||||
{
|
||||
_supportedMediaTypes = new List<string>
|
||||
{
|
||||
"application/json",
|
||||
"text/json"
|
||||
};
|
||||
|
||||
_supportedEncodings = new List<Encoding>
|
||||
{
|
||||
new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true),
|
||||
new UnicodeEncoding(bigEndian: false, byteOrderMark: true, throwOnInvalidBytes: true)
|
||||
};
|
||||
SupportedEncodings = new List<Encoding>();
|
||||
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/json"));
|
||||
|
||||
_jsonSerializerSettings = new JsonSerializerSettings
|
||||
{
|
||||
|
|
@ -48,16 +42,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<string> SupportedMediaTypes
|
||||
{
|
||||
get { return _supportedMediaTypes; }
|
||||
}
|
||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<Encoding> SupportedEncodings
|
||||
{
|
||||
get { return _supportedEncodings; }
|
||||
}
|
||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="JsonSerializerSettings"/> used to configure the <see cref="JsonSerializer"/>.
|
||||
|
|
@ -94,9 +82,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return;
|
||||
}
|
||||
|
||||
MediaTypeHeaderValue requestContentType = null;
|
||||
MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType);
|
||||
|
||||
// Get the character encoding for the content
|
||||
// Never non-null since SelectCharacterEncoding() throws in error / not found scenarios
|
||||
var effectiveEncoding = SelectCharacterEncoding(request.GetContentType());
|
||||
var effectiveEncoding = SelectCharacterEncoding(requestContentType);
|
||||
|
||||
context.Model = await ReadInternal(context, effectiveEncoding);
|
||||
}
|
||||
|
|
@ -124,12 +115,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return JsonSerializer.Create(SerializerSettings);
|
||||
}
|
||||
|
||||
private bool IsSupportedContentType(ContentTypeHeaderValue contentType)
|
||||
{
|
||||
return contentType != null &&
|
||||
_supportedMediaTypes.Contains(contentType.ContentType, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private Task<object> ReadInternal(InputFormatterContext context,
|
||||
Encoding effectiveEncoding)
|
||||
{
|
||||
|
|
@ -172,17 +157,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
private Encoding SelectCharacterEncoding(ContentTypeHeaderValue contentType)
|
||||
private Encoding SelectCharacterEncoding(MediaTypeHeaderValue contentType)
|
||||
{
|
||||
if (contentType != null)
|
||||
{
|
||||
// Find encoding based on content type charset parameter
|
||||
var charset = contentType.CharSet;
|
||||
if (!string.IsNullOrWhiteSpace(contentType.CharSet))
|
||||
var charset = contentType.Charset;
|
||||
if (!string.IsNullOrWhiteSpace(contentType.Charset))
|
||||
{
|
||||
for (var i = 0; i < _supportedEncodings.Count; i++)
|
||||
foreach (var supportedEncoding in SupportedEncodings)
|
||||
{
|
||||
var supportedEncoding = _supportedEncodings[i];
|
||||
if (string.Equals(charset, supportedEncoding.WebName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return supportedEncoding;
|
||||
|
|
@ -191,9 +175,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
}
|
||||
|
||||
if (_supportedEncodings.Count > 0)
|
||||
if (SupportedEncodings.Count > 0)
|
||||
{
|
||||
return _supportedEncodings[0];
|
||||
return SupportedEncodings[0];
|
||||
}
|
||||
|
||||
// No supported encoding was found so there is no way for us to start reading.
|
||||
|
|
|
|||
|
|
@ -1,52 +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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class TempInputFormatterProvider : IInputFormatterProvider
|
||||
{
|
||||
private IInputFormatter[] _formatters;
|
||||
|
||||
public IInputFormatter GetInputFormatter(InputFormatterProviderContext context)
|
||||
{
|
||||
var request = context.HttpContext.Request;
|
||||
|
||||
var formatters = _formatters;
|
||||
|
||||
if (formatters == null)
|
||||
{
|
||||
formatters = context.HttpContext.RequestServices.GetService<IEnumerable<IInputFormatter>>()
|
||||
.ToArray();
|
||||
|
||||
_formatters = formatters;
|
||||
}
|
||||
|
||||
var contentType = request.GetContentType();
|
||||
if (contentType == null)
|
||||
{
|
||||
// TODO: http exception?
|
||||
throw new InvalidOperationException("400: Bad Request");
|
||||
}
|
||||
|
||||
for (var i = 0; i < formatters.Length; i++)
|
||||
{
|
||||
var formatter = formatters[i];
|
||||
if (formatter.SupportedMediaTypes.Contains(contentType.ContentType, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return formatter;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Http exception
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
|
||||
"415: Unsupported content type {0}",
|
||||
contentType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ using System.Runtime.Serialization;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -18,8 +19,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public class XmlDataContractSerializerInputFormatter : IInputFormatter
|
||||
{
|
||||
private readonly IList<Encoding> _supportedEncodings;
|
||||
private readonly IList<string> _supportedMediaTypes;
|
||||
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -27,34 +26,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public XmlDataContractSerializerInputFormatter()
|
||||
{
|
||||
_supportedMediaTypes = new List<string>
|
||||
{
|
||||
"application/xml",
|
||||
"text/xml"
|
||||
};
|
||||
|
||||
_supportedEncodings = new List<Encoding>
|
||||
{
|
||||
Encodings.UTF8EncodingWithoutBOM,
|
||||
Encodings.UTF16EncodingLittleEndian
|
||||
};
|
||||
SupportedEncodings = new List<Encoding>();
|
||||
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of supported encodings.
|
||||
/// </summary>
|
||||
public IList<Encoding> SupportedEncodings
|
||||
{
|
||||
get { return _supportedEncodings; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of supported Media Types.
|
||||
/// </summary>
|
||||
public IList<string> SupportedMediaTypes
|
||||
{
|
||||
get { return _supportedMediaTypes; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the acceptable input XML depth.
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Text;
|
|||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -18,8 +19,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public class XmlSerializerInputFormatter : IInputFormatter
|
||||
{
|
||||
private readonly IList<Encoding> _supportedEncodings;
|
||||
private readonly IList<string> _supportedMediaTypes;
|
||||
private readonly XmlDictionaryReaderQuotas _readerQuotas = FormattingUtilities.GetDefaultXmlReaderQuotas();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -27,34 +26,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public XmlSerializerInputFormatter()
|
||||
{
|
||||
_supportedMediaTypes = new List<string>
|
||||
{
|
||||
"application/xml",
|
||||
"text/xml"
|
||||
};
|
||||
|
||||
_supportedEncodings = new List<Encoding>
|
||||
{
|
||||
Encodings.UTF8EncodingWithoutBOM,
|
||||
Encodings.UTF16EncodingLittleEndian
|
||||
};
|
||||
SupportedEncodings = new List<Encoding>();
|
||||
SupportedEncodings.Add(Encodings.UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(Encodings.UTF16EncodingLittleEndian);
|
||||
SupportedMediaTypes = new List<MediaTypeHeaderValue>();
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/xml"));
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/xml"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of supported encodings.
|
||||
/// </summary>
|
||||
public IList<Encoding> SupportedEncodings
|
||||
{
|
||||
get { return _supportedEncodings; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IList<MediaTypeHeaderValue> SupportedMediaTypes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the list of supported Media Types.
|
||||
/// </summary>
|
||||
public IList<string> SupportedMediaTypes
|
||||
{
|
||||
get { return _supportedMediaTypes; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IList<Encoding> SupportedEncodings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the acceptable input XML depth.
|
||||
|
|
@ -105,7 +89,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// <summary>
|
||||
/// Called during deserialization to get the <see cref="XmlSerializer"/>.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="XmlSerializer"/> used during serialization and deserialization.</returns>
|
||||
/// <returns>The <see cref="XmlSerializer"/> used during deserialization.</returns>
|
||||
protected virtual XmlSerializer CreateXmlSerializer(Type type)
|
||||
{
|
||||
return new XmlSerializer(type);
|
||||
|
|
|
|||
|
|
@ -1,19 +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.
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
internal class ContentTypeHeaderValue
|
||||
{
|
||||
public ContentTypeHeaderValue([NotNull] string contentType,
|
||||
string charSet)
|
||||
{
|
||||
ContentType = contentType;
|
||||
CharSet = charSet;
|
||||
}
|
||||
|
||||
public string ContentType { get; private set; }
|
||||
|
||||
public string CharSet { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +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 System;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
internal static class HttpRequestExtensions
|
||||
{
|
||||
private const string ContentTypeHeader = "Content-Type";
|
||||
private const string CharSetToken = "charset=";
|
||||
|
||||
public static ContentTypeHeaderValue GetContentType(this HttpRequest httpRequest)
|
||||
{
|
||||
var headerValue = httpRequest.Headers[ContentTypeHeader];
|
||||
if (!string.IsNullOrEmpty(headerValue))
|
||||
{
|
||||
var tokens = headerValue.Split(new[] { ';' }, 2);
|
||||
string charSet = null;
|
||||
if (tokens.Length > 1 &&
|
||||
tokens[1].TrimStart().StartsWith(CharSetToken, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
charSet = tokens[1].TrimStart().Substring(CharSetToken.Length);
|
||||
}
|
||||
return new ContentTypeHeaderValue(tokens[0], charSet);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,12 +4,14 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class FormValueProviderFactory : IValueProviderFactory
|
||||
{
|
||||
private const string FormEncodedContentType = "application/x-www-form-urlencoded";
|
||||
private static MediaTypeHeaderValue _formEncodedContentType =
|
||||
MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
|
||||
|
||||
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
|
||||
{
|
||||
|
|
@ -26,9 +28,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private bool IsSupportedContentType(HttpRequest request)
|
||||
{
|
||||
var contentType = request.GetContentType();
|
||||
return contentType != null &&
|
||||
string.Equals(contentType.ContentType, FormEncodedContentType, StringComparison.OrdinalIgnoreCase);
|
||||
MediaTypeHeaderValue requestContentType = null;
|
||||
return MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType) &&
|
||||
_formEncodedContentType.IsSubsetOf(requestContentType);
|
||||
}
|
||||
|
||||
private static CultureInfo GetCultureInfo(HttpRequest request)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
"Microsoft.AspNet.Mvc.Common": "",
|
||||
"Microsoft.DataAnnotations": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||
"Microsoft.AspNet.Mvc.HeaderValueAbstractions": "1.0.0-*",
|
||||
"Newtonsoft.Json": "5.0.8"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -1,7 +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 System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||
using Microsoft.AspNet.Mvc.Razor;
|
||||
using Microsoft.Framework.OptionsModel;
|
||||
|
||||
|
|
@ -43,6 +45,11 @@ namespace Microsoft.AspNet.Mvc
|
|||
options.OutputFormatters.Add(
|
||||
new XmlSerializerOutputFormatter(XmlOutputFormatter.GetDefaultXmlWriterSettings()));
|
||||
|
||||
// Set up default input formatters.
|
||||
options.InputFormatters.Add(new JsonInputFormatter());
|
||||
options.InputFormatters.Add(new XmlSerializerInputFormatter());
|
||||
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter());
|
||||
|
||||
// Set up ValueProviders
|
||||
options.ValueProviderFactories.Add(new RouteValueValueProviderFactory());
|
||||
options.ValueProviderFactories.Add(new QueryStringValueProviderFactory());
|
||||
|
|
|
|||
|
|
@ -64,10 +64,8 @@ namespace Microsoft.AspNet.Mvc
|
|||
yield return describe.Transient<IModelMetadataProvider, DataAnnotationsModelMetadataProvider>();
|
||||
yield return describe.Scoped<IActionBindingContextProvider, DefaultActionBindingContextProvider>();
|
||||
|
||||
yield return describe.Transient<IInputFormatter, JsonInputFormatter>();
|
||||
yield return describe.Transient<IInputFormatter, XmlSerializerInputFormatter>();
|
||||
yield return describe.Transient<IInputFormatter, XmlDataContractSerializerInputFormatter>();
|
||||
yield return describe.Transient<IInputFormatterProvider, TempInputFormatterProvider>();
|
||||
yield return describe.Transient<IInputFormattersProvider, DefaultInputFormattersProvider>();
|
||||
|
||||
yield return describe.Transient<IModelBinderProvider, DefaultModelBindersProvider>();
|
||||
yield return describe.Scoped<ICompositeModelBinder, CompositeModelBinder>();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
{
|
||||
public class InputFormatterDescriptorExtensionTest
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(-1)]
|
||||
[InlineData(5)]
|
||||
public void Insert_WithType_ThrowsIfIndexIsOutOfBounds(int index)
|
||||
{
|
||||
// Arrange
|
||||
var collection = new List<InputFormatterDescriptor>
|
||||
{
|
||||
new InputFormatterDescriptor(Mock.Of<IInputFormatter>()),
|
||||
new InputFormatterDescriptor(Mock.Of<IInputFormatter>())
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>("index",
|
||||
() => collection.Insert(index, typeof(IInputFormatter)));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(-2)]
|
||||
[InlineData(3)]
|
||||
public void Insert_WithInstance_ThrowsIfIndexIsOutOfBounds(int index)
|
||||
{
|
||||
// Arrange
|
||||
var collection = new List<InputFormatterDescriptor>
|
||||
{
|
||||
new InputFormatterDescriptor(Mock.Of<IInputFormatter>()),
|
||||
new InputFormatterDescriptor(Mock.Of<IInputFormatter>())
|
||||
};
|
||||
var formatter = Mock.Of<IInputFormatter>();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentOutOfRangeException>("index", () => collection.Insert(index, formatter));
|
||||
}
|
||||
|
||||
[InlineData]
|
||||
public void InputFormatterDescriptors_AddsTypesAndInstances()
|
||||
{
|
||||
// Arrange
|
||||
var formatter1 = Mock.Of<IInputFormatter>();
|
||||
var formatter2 = Mock.Of<IInputFormatter>();
|
||||
var type1 = typeof(JsonInputFormatter);
|
||||
var type2 = typeof(IInputFormatter);
|
||||
var collection = new List<InputFormatterDescriptor>();
|
||||
|
||||
// Act
|
||||
collection.Add(formatter1);
|
||||
collection.Insert(1, formatter2);
|
||||
collection.Add(type1);
|
||||
collection.Insert(2, type2);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(4, collection.Count);
|
||||
Assert.Equal(formatter1, collection[0].Instance);
|
||||
Assert.Equal(formatter2, collection[1].Instance);
|
||||
Assert.Equal(type2, collection[2].OptionType);
|
||||
Assert.Equal(type1, collection[3].OptionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// 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.OptionDescriptors;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class InputFormatterDescriptorTest
|
||||
{
|
||||
[Fact]
|
||||
public void ConstructorThrows_IfTypeIsNotInputFormatter()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "The type 'System.String' must derive from " +
|
||||
"'Microsoft.AspNet.Mvc.ModelBinding.IInputFormatter'.";
|
||||
|
||||
var type = typeof(string);
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgument(() => new InputFormatterDescriptor(type), "type", expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,23 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
|||
Assert.Equal(sampleInputInt.ToString(), await response.ReadBodyAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task JsonInputFormatter_IsSelectedForJsonRequest()
|
||||
{
|
||||
// Arrange
|
||||
var server = TestServer.Create(_services, _app);
|
||||
var client = server.Handler;
|
||||
var sampleInputInt = 10;
|
||||
var input = "{\"SampleInt\":10}";
|
||||
|
||||
// Act
|
||||
var response = await client.PostAsync("http://localhost/Home/Index", input, "application/json");
|
||||
|
||||
//Assert
|
||||
Assert.Equal(200, response.StatusCode);
|
||||
Assert.Equal(sampleInputInt.ToString(), await response.ReadBodyAsStringAsync());
|
||||
}
|
||||
|
||||
// TODO: By default XmlSerializerInputFormatter is called because of the order in which
|
||||
// the formatters are registered. Add a test to call into DataContractSerializerInputFormatter.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var mediaType = formatter.SupportedMediaTypes[0];
|
||||
|
||||
// Assert
|
||||
Assert.Equal("application/json", mediaType);
|
||||
Assert.Equal("application/json", mediaType.RawValue);
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> JsonFormatterReadSimpleTypesData
|
||||
|
|
|
|||
|
|
@ -50,8 +50,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var formatter = new XmlDataContractSerializerInputFormatter();
|
||||
|
||||
// Assert
|
||||
Assert.True(formatter.SupportedMediaTypes.Contains("application/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes.Contains("text/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes
|
||||
.Select(content => content.RawValue)
|
||||
.Contains("application/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes
|
||||
.Select(content => content.RawValue)
|
||||
.Contains("text/xml"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -41,8 +41,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var formatter = new XmlSerializerInputFormatter();
|
||||
|
||||
// Assert
|
||||
Assert.True(formatter.SupportedMediaTypes.Contains("application/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes.Contains("text/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes
|
||||
.Select(content => content.RawValue)
|
||||
.Contains("application/xml"));
|
||||
Assert.True(formatter.SupportedMediaTypes
|
||||
.Select(content => content.RawValue)
|
||||
.Contains("text/xml"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -52,10 +52,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var collection = Mock.Of<IReadableStringCollection>();
|
||||
var request = new Mock<HttpRequest>();
|
||||
request.Setup(f => f.GetFormAsync(CancellationToken.None)).Returns(Task.FromResult(collection));
|
||||
|
||||
var mockHeader = new Mock<IHeaderDictionary>();
|
||||
mockHeader.Setup(h => h["Content-Type"]).Returns(contentType);
|
||||
request.SetupGet(r => r.Headers).Returns(mockHeader.Object);
|
||||
request.SetupGet(r => r.ContentType).Returns(contentType);
|
||||
|
||||
var context = new Mock<HttpContext>();
|
||||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||||
|
|
|
|||
|
|
@ -80,5 +80,22 @@ namespace Microsoft.AspNet.Mvc
|
|||
Assert.IsType<XmlDataContractSerializerOutputFormatter>(mvcOptions.OutputFormatters[3].Instance);
|
||||
Assert.IsType<XmlSerializerOutputFormatter>(mvcOptions.OutputFormatters[4].Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Setup_SetsUpInputFormatters()
|
||||
{
|
||||
// Arrange
|
||||
var mvcOptions = new MvcOptions();
|
||||
var setup = new MvcOptionsSetup();
|
||||
|
||||
// Act
|
||||
setup.Setup(mvcOptions);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(3, mvcOptions.InputFormatters.Count);
|
||||
Assert.IsType<JsonInputFormatter>(mvcOptions.InputFormatters[0].Instance);
|
||||
Assert.IsType<XmlSerializerInputFormatter>(mvcOptions.InputFormatters[1].Instance);
|
||||
Assert.IsType<XmlDataContractSerializerInputFormatter>(mvcOptions.InputFormatters[2].Instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue