1. Moving input formatting related classes to MVC Core.(for tests as well).

2. Cleaning up the IInputFormatter to the final version.
3. Updating the input formatters and the context to be compliant with the IInputFormatter interface.
4. Adding Functional Tests.
- Not cleaning up TempInputFormatterProvider.

Conflicts:
	src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatter.cs
	src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs
	src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs
	src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerInputFormatter.cs
	src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerInputFormatter.cs
	src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj
	src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs
	src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj
	test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj

Conflicts:
	src/Microsoft.AspNet.Mvc.Core/Microsoft.AspNet.Mvc.Core.kproj
	src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj
	src/Microsoft.AspNet.Mvc.ModelBinding/Microsoft.AspNet.Mvc.ModelBinding.kproj
	src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/FormValueProviderFactory.cs
	test/Microsoft.AspNet.Mvc.Core.Test/Microsoft.AspNet.Mvc.Core.Test.kproj
	test/Microsoft.AspNet.Mvc.ModelBinding.Test/Microsoft.AspNet.Mvc.ModelBinding.Test.kproj

Conflicts:
	src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs
	src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs
This commit is contained in:
harshgMSFT 2014-08-08 15:57:19 -07:00
parent 4f69e17f53
commit 64d797a489
38 changed files with 535 additions and 264 deletions

View File

@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Mvc
var incomingAcceptHeader = HeaderParsingHelpers.GetAcceptHeaders(
formatterContext.ActionContext.HttpContext.Request.Accept);
var sortedAcceptHeaders = SortMediaTypeWithQualityHeaderValues(incomingAcceptHeader)
.Where(header => header.Quality != FormattingUtilities.NoMatch)
.Where(header => header.Quality != HttpHeaderUtilitites.NoMatch)
.ToArray();
IOutputFormatter selectedFormatter = null;

View File

@ -241,18 +241,16 @@ namespace Microsoft.AspNet.Mvc
modelAccessor: null,
modelType: parameterType);
var providerContext = new InputFormatterProviderContext(
actionBindingContext.ActionContext.HttpContext,
actionBindingContext.ActionContext,
modelMetadata,
modelState);
var inputFormatter = actionBindingContext.InputFormatterProvider.GetInputFormatter(
providerContext);
var formatterContext = new InputFormatterContext(actionBindingContext.ActionContext.HttpContext,
modelMetadata,
modelState);
await inputFormatter.ReadAsync(formatterContext);
parameterValues[parameter.Name] = formatterContext.Model;
var formatterContext = new InputFormatterContext(actionBindingContext.ActionContext,
modelMetadata.ModelType);
parameterValues[parameter.Name] = await inputFormatter.ReadAsync(formatterContext);
}
else
{

View File

@ -6,7 +6,7 @@ using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Stream that delegates to an inner stream.

View File

@ -5,9 +5,10 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Contains methods which are used by input formatters.

View File

@ -0,0 +1,33 @@
// 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 System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Reads an object from the request body.
/// </summary>
public interface IInputFormatter
{
/// <summary>
/// Determines whether this <see cref="IInputFormatter"/> can de-serialize
/// an object of the specified type.
/// </summary>
/// <param name="context">Input formatter context associated with this call.</param>
/// <returns>True if this <see cref="IInputFormatter"/> supports the passed in
/// request's content-type and is able to de-serialize the request body.
/// False otherwise.</returns>
bool CanRead(InputFormatterContext context);
/// <summary>
/// Called during deserialization to read an object from the request.
/// </summary>
/// <param name="context">Input formatter context associated with this call.</param>
/// <returns>A task that deserializes the request body.</returns>
Task<object> ReadAsync(InputFormatterContext context);
}
}

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public interface IInputFormatterProvider
{

View File

@ -0,0 +1,37 @@
// 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.Text;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// Represents information used by an input formatter for
/// deserializing the request body into an object.
/// </summary>
public class InputFormatterContext
{
/// <summary>
/// Creates a new instance of <see cref="InputFormatterContext"/>.
/// </summary>
public InputFormatterContext([NotNull] ActionContext actionContext,
[NotNull] Type modelType)
{
ActionContext = actionContext;
ModelType = modelType;
}
/// <summary>
/// Action context associated with the current call.
/// </summary>
public ActionContext ActionContext { get; private set; }
/// <summary>
/// Represents the expected type of the model represented by the request body.
/// </summary>
public Type ModelType { get; private set; }
}
}

View File

@ -2,21 +2,22 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public class InputFormatterProviderContext
{
public InputFormatterProviderContext([NotNull] HttpContext httpContext,
public InputFormatterProviderContext([NotNull] ActionContext actionContext,
[NotNull] ModelMetadata metadata,
[NotNull] ModelStateDictionary modelState)
{
HttpContext = httpContext;
ActionContext = actionContext;
Metadata = metadata;
ModelState = modelState;
}
public HttpContext HttpContext { get; private set; }
public ActionContext ActionContext { get; private set; }
public ModelMetadata Metadata { get; private set; }

View File

@ -8,10 +8,11 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
using Newtonsoft.Json;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public class JsonInputFormatter : IInputFormatter
{
@ -66,20 +67,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// <summary>
/// Gets or sets if deserialization errors are captured. When set, these errors appear in
/// the <see cref="ModelStateDictionary"/> instance of <see cref="InputFormatterContext"/>.
/// the <see cref="ActionContext.ModelState"/> instance of <see cref="InputFormatterContext"/>.
/// </summary>
public bool CaptureDeserilizationErrors { get; set; }
/// <inheritdoc />
public async Task ReadAsync([NotNull] InputFormatterContext context)
public bool CanRead(InputFormatterContext context)
{
var request = context.HttpContext.Request;
var contentType = context.ActionContext.HttpContext.Request.ContentType;
MediaTypeHeaderValue requestContentType;
if (!MediaTypeHeaderValue.TryParse(contentType, out requestContentType))
{
return false;
}
return SupportedMediaTypes
.Any(supportedMediaType => supportedMediaType.IsSubsetOf(requestContentType));
}
/// <inheritdoc />
public async Task<object> ReadAsync([NotNull] InputFormatterContext context)
{
var request = context.ActionContext.HttpContext.Request;
if (request.ContentLength == 0)
{
var modelType = context.Metadata.ModelType;
context.Model = modelType.GetTypeInfo().IsValueType ? Activator.CreateInstance(modelType) :
var modelType = context.ModelType;
var model = modelType.GetTypeInfo().IsValueType ? Activator.CreateInstance(modelType) :
null;
return;
return model;
}
MediaTypeHeaderValue requestContentType = null;
@ -89,7 +104,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Never non-null since SelectCharacterEncoding() throws in error / not found scenarios
var effectiveEncoding = SelectCharacterEncoding(requestContentType);
context.Model = await ReadInternal(context, effectiveEncoding);
return await ReadInternal(context, effectiveEncoding);
}
/// <summary>
@ -118,8 +133,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private Task<object> ReadInternal(InputFormatterContext context,
Encoding effectiveEncoding)
{
var type = context.Metadata.ModelType;
var request = context.HttpContext.Request;
var type = context.ModelType;
var request = context.ActionContext.HttpContext.Request;
using (var jsonReader = CreateJsonReader(context, request.Body, effectiveEncoding))
{
@ -133,7 +148,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
errorHandler = (sender, e) =>
{
var exception = e.ErrorContext.Error;
context.ModelState.AddModelError(e.ErrorContext.Path, e.ErrorContext.Error);
context.ActionContext.ModelState.AddModelError(e.ErrorContext.Path, e.ErrorContext.Error);
// Error must always be marked as handled
// Failure to do so can cause the exception to be rethrown at every recursive level and
// overflow the stack for x64 CLR processes
@ -181,7 +196,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
}
// No supported encoding was found so there is no way for us to start reading.
throw new InvalidOperationException(Resources.FormatMediaTypeFormatterNoEncoding(GetType().FullName));
throw new InvalidOperationException(Resources.FormatInputFormatterNoEncoding(GetType().FullName));
}
}
}

View File

@ -85,8 +85,8 @@ namespace Microsoft.AspNet.Mvc
private static int CompareBasedOnQualityFactor(MediaTypeWithQualityHeaderValue mediaType1,
MediaTypeWithQualityHeaderValue mediaType2)
{
var mediaType1Quality = mediaType1.Quality ?? FormattingUtilities.Match;
var mediaType2Quality = mediaType2.Quality ?? FormattingUtilities.Match;
var mediaType1Quality = mediaType1.Quality ?? HttpHeaderUtilitites.Match;
var mediaType2Quality = mediaType2.Quality ?? HttpHeaderUtilitites.Match;
var qualityDifference = mediaType1Quality - mediaType2Quality;
if (qualityDifference < 0)
{

View File

@ -150,7 +150,7 @@ namespace Microsoft.AspNet.Mvc
{
var sortedAcceptCharsetHeaders = acceptCharsetHeaders
.Where(acceptCharset =>
acceptCharset.Quality != FormattingUtilities.NoMatch)
acceptCharset.Quality != HttpHeaderUtilitites.NoMatch)
.OrderByDescending(
m => m, StringWithQualityHeaderValueComparer.QualityComparer);

View File

@ -42,8 +42,8 @@ namespace Microsoft.AspNet.Mvc
public int Compare([NotNull] StringWithQualityHeaderValue stringWithQuality1,
[NotNull] StringWithQualityHeaderValue stringWithQuality2)
{
var quality1 = stringWithQuality1.Quality ?? FormattingUtilities.Match;
var quality2 = stringWithQuality2.Quality ?? FormattingUtilities.Match;
var quality1 = stringWithQuality1.Quality ?? HttpHeaderUtilitites.Match;
var quality2 = stringWithQuality2.Quality ?? HttpHeaderUtilitites.Match;
var qualityDifference = quality1 - quality2;
if (qualityDifference < 0)
{

View File

@ -2,13 +2,7 @@
// 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
{
@ -23,20 +17,15 @@ namespace Microsoft.AspNet.Mvc
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 request = context.ActionContext.HttpContext.Request;
var formatterContext = new InputFormatterContext(context.ActionContext,
context.Metadata.ModelType);
// TODO: https://github.com/aspnet/Mvc/issues/1014
var formatters = _defaultFormattersProvider.InputFormatters;
foreach (var formatter in formatters)
{
var formatterMatched = formatter.SupportedMediaTypes
.Any(supportedMediaType =>
supportedMediaType.IsSubsetOf(contentType));
var formatterMatched = formatter.CanRead(formatterContext);
if (formatterMatched)
{
return formatter;
@ -46,7 +35,7 @@ namespace Microsoft.AspNet.Mvc
// TODO: https://github.com/aspnet/Mvc/issues/458
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
"415: Unsupported content type {0}",
contentType.RawValue));
request.ContentType));
}
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
@ -11,7 +12,7 @@ using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// This class handles deserialization of input XML data
@ -58,21 +59,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
get { return _readerQuotas; }
}
/// <inheritdoc />
public bool CanRead(InputFormatterContext context)
{
var contentType = context.ActionContext.HttpContext.Request.ContentType;
MediaTypeHeaderValue requestContentType;
if (!MediaTypeHeaderValue.TryParse(contentType, out requestContentType))
{
return false;
}
return SupportedMediaTypes
.Any(supportedMediaType => supportedMediaType.IsSubsetOf(requestContentType));
}
/// <summary>
/// Reads the input XML.
/// </summary>
/// <param name="context">The input formatter context which contains the body to be read.</param>
/// <returns>Task which reads the input.</returns>
public async Task ReadAsync(InputFormatterContext context)
public async Task<object> ReadAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
var request = context.ActionContext.HttpContext.Request;
if (request.ContentLength == 0)
{
context.Model = GetDefaultValueForType(context.Metadata.ModelType);
return;
return GetDefaultValueForType(context.ModelType);
}
context.Model = await ReadInternal(context);
return await ReadInternal(context);
}
/// <summary>
@ -107,8 +121,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private Task<object> ReadInternal(InputFormatterContext context)
{
var type = context.Metadata.ModelType;
var request = context.HttpContext.Request;
var type = context.ModelType;
var request = context.ActionContext.HttpContext.Request;
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
@ -11,7 +12,7 @@ using System.Xml;
using System.Xml.Serialization;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
/// <summary>
/// This class handles deserialization of input XML data
@ -58,21 +59,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
get { return _readerQuotas; }
}
/// <inheritdoc />
public bool CanRead(InputFormatterContext context)
{
var contentType = context.ActionContext.HttpContext.Request.ContentType;
MediaTypeHeaderValue requestContentType;
if (!MediaTypeHeaderValue.TryParse(contentType, out requestContentType))
{
return false;
}
return SupportedMediaTypes
.Any(supportedMediaType => supportedMediaType.IsSubsetOf(requestContentType));
}
/// <summary>
/// Reads the input XML.
/// </summary>
/// <param name="context">The input formatter context which contains the body to be read.</param>
/// <returns>Task which reads the input.</returns>
public async Task ReadAsync(InputFormatterContext context)
public async Task<object> ReadAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
var request = context.ActionContext.HttpContext.Request;
if (request.ContentLength == 0)
{
context.Model = GetDefaultValueForType(context.Metadata.ModelType);
return;
return GetDefaultValueForType(context.ModelType);
}
context.Model = await ReadInternal(context);
return await ReadInternal(context);
}
/// <summary>
@ -107,8 +121,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
private Task<object> ReadInternal(InputFormatterContext context)
{
var type = context.Metadata.ModelType;
var request = context.HttpContext.Request;
var type = context.ModelType;
var request = context.ActionContext.HttpContext.Request;
using (var xmlReader = CreateXmlReader(new DelegatingStream(request.Body)))
{

View File

@ -51,8 +51,16 @@ namespace Microsoft.AspNet.Mvc
}
}
/// <summary>
/// Get a list of the <see cref="OutputFormatterDescriptor" /> which are used to construct
/// a list of <see cref="IOutputFormatter"/> by <see cref="IOutputFormattersProvider"/>.
/// </summary>
public List<OutputFormatterDescriptor> OutputFormatters { get; private set; }
/// <summary>
/// Get a list of the <see cref="InputFormatterDescriptor" /> which are used to construct
/// a list of <see cref="IInputFormatter"/> by <see cref="IInputFormatterProvider"/>.
/// </summary>
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
/// <summary>

View File

@ -1066,6 +1066,22 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("OutputFormatterNoEncoding"), p0);
}
/// <summary>
/// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.
/// </summary>
internal static string InputFormatterNoEncoding
{
get { return GetString("InputFormatterNoEncoding"); }
}
/// <summary>
/// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.
/// </summary>
internal static string FormatInputFormatterNoEncoding(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("InputFormatterNoEncoding"), p0);
}
/// <summary>
/// No supported media type registered for output formatter '{0}'. There must be at least one supported media type registered in order for the output formatter to write content.
/// </summary>

View File

@ -81,4 +81,4 @@ namespace Microsoft.AspNet.Mvc
};
}
}
}
}

View File

@ -315,6 +315,9 @@
<data name="OutputFormatterNoEncoding" xml:space="preserve">
<value>No encoding found for output formatter '{0}'. There must be at least one supported encoding registered in order for the output formatter to write content.</value>
</data>
<data name="InputFormatterNoEncoding" xml:space="preserve">
<value>No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.</value>
</data>
<data name="OutputFormatterNoMediaType" xml:space="preserve">
<value>No supported media type registered for output formatter '{0}'. There must be at least one supported media type registered in order for the output formatter to write content.</value>
</data>

View File

@ -3,7 +3,7 @@
namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
{
public static class FormattingUtilities
public static class HttpHeaderUtilitites
{
/// <summary>
/// Quality factor to indicate a perfect match.

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
return null;
}
var quality = FormattingUtilities.Match;
var quality = HttpHeaderUtilitites.Match;
string qualityStringValue = null;
if (mediaTypeHeaderValue.Parameters.TryGetValue("q", out qualityStringValue))
{

View File

@ -16,10 +16,5 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
var value = inputArray[0].Trim();
// Unspecified q factor value is equal to a match.
var quality = FormattingUtilities.Match;
var quality = HttpHeaderUtilitites.Match;
if (inputArray.Length > 1)
{
var parameter = inputArray[1].Trim();

View File

@ -1,29 +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.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public interface IInputFormatter
{
/// <summary>
/// Gets the mutable collection of media types supported by this <see cref="JsonInputFormatter"/> instance.
/// </summary>
IList<MediaTypeHeaderValue> SupportedMediaTypes { get; }
/// <summary>
/// Gets the mutable collection of character encodings supported by this <see cref="JsonInputFormatter"/>
/// instance.
/// </summary>
IList<Encoding> SupportedEncodings { get; }
/// <summary>
/// Called during deserialization to read an object from the request.
/// </summary>
Task ReadAsync(InputFormatterContext context);
}
}

View File

@ -1,29 +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.Text;
using Microsoft.AspNet.Http;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class InputFormatterContext
{
public InputFormatterContext([NotNull] HttpContext httpContext,
[NotNull] ModelMetadata metadata,
[NotNull] ModelStateDictionary modelState)
{
HttpContext = httpContext;
Metadata = metadata;
ModelState = modelState;
}
public HttpContext HttpContext { get; private set; }
public ModelMetadata Metadata { get; private set; }
public ModelStateDictionary ModelState { get; private set; }
public object Model { get; set; }
}
}

View File

@ -74,22 +74,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return GetString("JQuerySyntaxMissingClosingBracket");
}
/// <summary>
/// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.
/// </summary>
internal static string MediaTypeFormatterNoEncoding
{
get { return GetString("MediaTypeFormatterNoEncoding"); }
}
/// <summary>
/// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.
/// </summary>
internal static string FormatMediaTypeFormatterNoEncoding(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("MediaTypeFormatterNoEncoding"), p0);
}
/// <summary>
/// Property '{0}' on type '{1}' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].
/// </summary>

View File

@ -129,9 +129,6 @@
<data name="JQuerySyntaxMissingClosingBracket" xml:space="preserve">
<value>The key is invalid JQuery syntax because it is missing a closing bracket.</value>
</data>
<data name="MediaTypeFormatterNoEncoding" xml:space="preserve">
<value>No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content.</value>
</data>
<data name="MissingDataMemberIsRequired" xml:space="preserve">
<value>Property '{0}' on type '{1}' is invalid. Value-typed properties marked as [Required] must also be marked with [DataMember(IsRequired=true)] to be recognized as required. Consider attributing the declaring type with [DataContract] and the property with [DataMember(IsRequired=true)].</value>
</data>

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public class FormValueProviderFactory : IValueProviderFactory
{
private static MediaTypeHeaderValue _formEncodedContentType =
MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
public IValueProvider GetValueProvider([NotNull] ValueProviderFactoryContext context)
{
@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
MediaTypeHeaderValue requestContentType = null;
return MediaTypeHeaderValue.TryParse(request.ContentType, out requestContentType) &&
_formEncodedContentType.IsSubsetOf(requestContentType);
_formEncodedContentType.IsSubsetOf(requestContentType);
}
private static CultureInfo GetCultureInfo(HttpRequest request)

View File

@ -5,7 +5,7 @@
using System.IO;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
namespace Microsoft.AspNet.Mvc
{
public class DelegatingStreamTests
{

View File

@ -8,14 +8,43 @@ using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Moq;
using Newtonsoft.Json;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public class JsonInputFormatterTest
{
{
[Theory]
[InlineData("application/json", true)]
[InlineData("application/*", true)]
[InlineData("*/*", true)]
[InlineData("text/json", true)]
[InlineData("text/*", true)]
[InlineData("text/xml", false)]
[InlineData("application/xml", false)]
[InlineData("", false)]
[InlineData(null, false)]
[InlineData("invalid", false)]
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
{
// Arrange
var formatter = new JsonInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes("content");
var actionContext = GetActionContext(contentBytes, contentType: requestContentType);
var formatterContext = new InputFormatterContext(actionContext, typeof(string));
// Act
var result = formatter.CanRead(formatterContext);
// Assert
Assert.Equal(expectedCanRead, result);
}
[Fact]
public void DefaultMediaType_ReturnsApplicationJson()
{
@ -48,16 +77,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var formatter = new JsonInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, type);
var context = new InputFormatterContext(httpContext, metadata, modelState);
var actionContext = GetActionContext(contentBytes);
var context = new InputFormatterContext(actionContext, type);
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.Equal(expected, context.Model);
Assert.Equal(expected, model);
}
[Fact]
@ -68,18 +95,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var formatter = new JsonInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var context = new InputFormatterContext(httpContext, metadata, modelState);
var context = new InputFormatterContext(actionContext, metadata.ModelType);
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
var model = Assert.IsType<User>(context.Model);
Assert.Equal("Person Name", model.Name);
Assert.Equal(30, model.Age);
var userModel = Assert.IsType<User>(model);
Assert.Equal("Person Name", userModel.Name);
Assert.Equal(30, userModel.Age);
}
[Fact]
@ -90,10 +116,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var formatter = new JsonInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var httpContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var context = new InputFormatterContext(httpContext, metadata, modelState);
var context = new InputFormatterContext(httpContext, metadata.ModelType);
// Act and Assert
await Assert.ThrowsAsync<JsonReaderException>(() => formatter.ReadAsync(context));
@ -107,17 +132,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var formatter = new JsonInputFormatter { CaptureDeserilizationErrors = true };
var contentBytes = Encoding.UTF8.GetBytes(content);
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, typeof(User));
var context = new InputFormatterContext(httpContext, metadata, modelState);
var context = new InputFormatterContext(actionContext, metadata.ModelType);
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.Equal("Could not convert string to decimal: not-an-age. Path 'Age', line 1, position 39.",
modelState["Age"].Errors[0].Exception.Message);
Assert.Equal("Could not convert string to decimal: not-an-age. Path 'Age', line 1, position 39.",
actionContext.ModelState["Age"].Errors[0].Exception.Message);
}
private static ActionContext GetActionContext(byte[] contentBytes,
string contentType = "application/xml")
{
return new ActionContext(GetHttpContext(contentBytes, contentType),
new AspNet.Routing.RouteData(),
new ActionDescriptor());
}
private static HttpContext GetHttpContext(byte[] contentBytes,
@ -125,12 +157,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
{
var request = new Mock<HttpRequest>();
var headers = new Mock<IHeaderDictionary>();
headers.SetupGet(h => h["Content-Type"]).Returns(contentType);
request.SetupGet(r => r.Headers).Returns(headers.Object);
request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes));
request.SetupGet(f => f.ContentType).Returns(contentType);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.Request).Returns(request.Object);
return httpContext.Object;
}

View File

@ -10,10 +10,11 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public class DataContractSerializerInputFormatterTests
{
@ -43,6 +44,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public TestLevelOne TestOne { get; set; }
}
[Theory]
[InlineData("application/xml", true)]
[InlineData("application/*", true)]
[InlineData("*/*", true)]
[InlineData("text/xml", true)]
[InlineData("text/*", true)]
[InlineData("text/json", false)]
[InlineData("application/json", false)]
[InlineData("", false)]
[InlineData(null, false)]
[InlineData("invalid", false)]
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
{
// Arrange
var formatter = new XmlDataContractSerializerInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes("content");
var actionContext = GetActionContext(contentBytes, contentType: requestContentType);
var formatterContext = new InputFormatterContext(actionContext, typeof(string));
// Act
var result = formatter.CanRead(formatterContext);
// Assert
Assert.Equal(expectedCanRead, result);
}
[Fact]
public void XmlDataContractSerializerFormatterHasProperSuppportedMediaTypes()
{
@ -85,15 +113,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelOne>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelOne>(model);
var model = context.Model as TestLevelOne;
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
var levelOneModel = model as TestLevelOne;
Assert.Equal(expectedInt, levelOneModel.SampleInt);
Assert.Equal(expectedString, levelOneModel.sampleString);
}
[Fact]
@ -114,16 +142,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelTwo>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelTwo>(model);
var model = context.Model as TestLevelTwo;
Assert.Equal(expectedLevelTwoString, model.SampleString);
Assert.Equal(expectedInt, model.TestOne.SampleInt);
Assert.Equal(expectedString, model.TestOne.sampleString);
var levelTwoModel = model as TestLevelTwo;
Assert.Equal(expectedLevelTwoString, levelTwoModel.SampleString);
Assert.Equal(expectedInt, levelTwoModel.TestOne.SampleInt);
Assert.Equal(expectedString, levelTwoModel.TestOne.sampleString);
}
[Fact]
@ -141,13 +169,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<DummyClass>(context.Model);
var model = context.Model as DummyClass;
Assert.Equal(expectedInt, model.SampleInt);
Assert.NotNull(model);
Assert.IsType<DummyClass>(model);
var dummyModel = model as DummyClass;
Assert.Equal(expectedInt, dummyModel.SampleInt);
}
[Fact]
@ -205,11 +233,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.True(context.HttpContext.Request.Body.CanRead);
Assert.NotNull(model);
Assert.True(context.ActionContext.HttpContext.Request.Body.CanRead);
}
[Fact]
@ -254,14 +282,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
var model = context.Model as TestLevelTwo;
Assert.NotNull(model);
var levelTwoModel = model as TestLevelTwo;
Buffer.BlockCopy(sampleStringBytes, 0, expectedBytes, 0, sampleStringBytes.Length);
Buffer.BlockCopy(bom, 0, expectedBytes, sampleStringBytes.Length, bom.Length);
Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(model.SampleString));
Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(levelTwoModel.SampleString));
}
[Fact]
@ -280,36 +308,43 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelOne>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelOne>(model);
var model = context.Model as TestLevelOne;
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
var levelOneModel = model as TestLevelOne;
Assert.Equal(expectedInt, levelOneModel.SampleInt);
Assert.Equal(expectedString, levelOneModel.sampleString);
}
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType);
return new InputFormatterContext(httpContext, metadata, modelState);
return new InputFormatterContext(actionContext, metadata.ModelType);
}
private static ActionContext GetActionContext(byte[] contentBytes,
string contentType = "application/xml")
{
return new ActionContext(GetHttpContext(contentBytes, contentType),
new AspNet.Routing.RouteData(),
new ActionDescriptor());
}
private static HttpContext GetHttpContext(byte[] contentBytes,
string contentType = "application/xml")
string contentType = "application/xml")
{
var request = new Mock<HttpRequest>();
var headers = new Mock<IHeaderDictionary>();
headers.SetupGet(h => h["Content-Type"]).Returns(contentType);
request.SetupGet(r => r.Headers).Returns(headers.Object);
request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes));
request.SetupGet(f => f.ContentType).Returns(contentType);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.Request).Returns(request.Object);
return httpContext.Object;
}
}

View File

@ -9,10 +9,11 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Mvc.ModelBinding;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
namespace Microsoft.AspNet.Mvc
{
public class XmlSerializerInputFormatterTests
{
@ -34,6 +35,33 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
public TestLevelOne TestOne { get; set; }
}
[Theory]
[InlineData("application/xml", true)]
[InlineData("application/*", true)]
[InlineData("*/*", true)]
[InlineData("text/xml", true)]
[InlineData("text/*", true)]
[InlineData("text/json", false)]
[InlineData("application/json", false)]
[InlineData("", false)]
[InlineData("invalid", false)]
[InlineData(null, false)]
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
{
// Arrange
var formatter = new XmlSerializerInputFormatter();
var contentBytes = Encoding.UTF8.GetBytes("content");
var actionContext = GetActionContext(contentBytes, contentType: requestContentType);
var formatterContext = new InputFormatterContext(actionContext, typeof(string));
// Act
var result = formatter.CanRead(formatterContext);
// Assert
Assert.Equal(expectedCanRead, result);
}
[Fact]
public void XmlSerializerFormatterHasProperSuppportedMediaTypes()
{
@ -78,16 +106,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelOne>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelOne>(model);
var model = context.Model as TestLevelOne;
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), model.SampleDate);
var levelOneModel = model as TestLevelOne;
Assert.Equal(expectedInt, levelOneModel.SampleInt);
Assert.Equal(expectedString, levelOneModel.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
levelOneModel.SampleDate);
}
[Fact]
@ -110,17 +139,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelTwo>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelTwo>(model);
var model = context.Model as TestLevelTwo;
Assert.Equal(expectedLevelTwoString, model.SampleString);
Assert.Equal(expectedInt, model.TestOne.SampleInt);
Assert.Equal(expectedString, model.TestOne.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), model.TestOne.SampleDate);
var levelTwoModel = model as TestLevelTwo;
Assert.Equal(expectedLevelTwoString, levelTwoModel.SampleString);
Assert.Equal(expectedInt, levelTwoModel.TestOne.SampleInt);
Assert.Equal(expectedString, levelTwoModel.TestOne.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc),
levelTwoModel.TestOne.SampleDate);
}
[Fact]
@ -138,13 +168,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<DummyClass>(context.Model);
var model = context.Model as DummyClass;
Assert.Equal(expectedInt, model.SampleInt);
Assert.NotNull(model);
Assert.IsType<DummyClass>(model);
var dummyModel = model as DummyClass;
Assert.Equal(expectedInt, dummyModel.SampleInt);
}
[Fact]
@ -206,11 +236,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(DummyClass));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.True(context.HttpContext.Request.Body.CanRead);
Assert.NotNull(model);
Assert.True(context.ActionContext.HttpContext.Request.Body.CanRead);
}
[Fact]
@ -255,14 +285,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelTwo));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
var model = context.Model as TestLevelTwo;
Assert.NotNull(model);
var levelTwoModel = model as TestLevelTwo;
Buffer.BlockCopy(sampleStringBytes, 0, expectedBytes, 0, sampleStringBytes.Length);
Buffer.BlockCopy(bom, 0, expectedBytes, sampleStringBytes.Length, bom.Length);
Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(model.SampleString));
Assert.Equal(expectedBytes, Encoding.UTF8.GetBytes(levelTwoModel.SampleString));
}
[Fact]
@ -283,37 +313,44 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var context = GetInputFormatterContext(contentBytes, typeof(TestLevelOne));
// Act
await formatter.ReadAsync(context);
var model = await formatter.ReadAsync(context);
// Assert
Assert.NotNull(context.Model);
Assert.IsType<TestLevelOne>(context.Model);
Assert.NotNull(model);
Assert.IsType<TestLevelOne>(model);
var model = context.Model as TestLevelOne;
Assert.Equal(expectedInt, model.SampleInt);
Assert.Equal(expectedString, model.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), model.SampleDate);
var levelOneModel = model as TestLevelOne;
Assert.Equal(expectedInt, levelOneModel.SampleInt);
Assert.Equal(expectedString, levelOneModel.sampleString);
Assert.Equal(XmlConvert.ToDateTime(expectedDateTime, XmlDateTimeSerializationMode.Utc), levelOneModel.SampleDate);
}
private InputFormatterContext GetInputFormatterContext(byte[] contentBytes, Type modelType)
{
var httpContext = GetHttpContext(contentBytes);
var modelState = new ModelStateDictionary();
var actionContext = GetActionContext(contentBytes);
var metadata = new EmptyModelMetadataProvider().GetMetadataForType(null, modelType);
return new InputFormatterContext(httpContext, metadata, modelState);
return new InputFormatterContext(actionContext, metadata.ModelType);
}
private static ActionContext GetActionContext(byte[] contentBytes,
string contentType = "application/xml")
{
return new ActionContext(GetHttpContext(contentBytes, contentType),
new AspNet.Routing.RouteData(),
new ActionDescriptor());
}
private static HttpContext GetHttpContext(byte[] contentBytes,
string contentType = "application/xml")
string contentType = "application/xml")
{
var request = new Mock<HttpRequest>();
var headers = new Mock<IHeaderDictionary>();
headers.SetupGet(h => h["Content-Type"]).Returns(contentType);
request.SetupGet(r => r.Headers).Returns(headers.Object);
request.SetupGet(f => f.Body).Returns(new MemoryStream(contentBytes));
request.SetupGet(f => f.ContentType).Returns(contentType);
var httpContext = new Mock<HttpContext>();
httpContext.SetupGet(c => c.Request).Returns(request.Object);
httpContext.SetupGet(c => c.Request).Returns(request.Object);
return httpContext.Object;
}
}

View File

@ -1,6 +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;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.AspNet.Testing;
using Xunit;
@ -14,12 +17,53 @@ namespace Microsoft.AspNet.Mvc
{
// Arrange
var expected = "The type 'System.String' must derive from " +
"'Microsoft.AspNet.Mvc.ModelBinding.IInputFormatter'.";
"'Microsoft.AspNet.Mvc.IInputFormatter'.";
var type = typeof(string);
// Act & Assert
ExceptionAssert.ThrowsArgument(() => new InputFormatterDescriptor(type), "type", expected);
}
[Fact]
public void ConstructorSets_InputFormatterType()
{
// Arrange
var type = typeof(TestInputFormatter);
// Act
var descriptor = new InputFormatterDescriptor(type);
// Assert
Assert.Equal(type, descriptor.OptionType);
Assert.Null(descriptor.Instance);
}
[Fact]
public void ConstructorSets_InputFormatterInstanceAndType()
{
// Arrange
var testFormatter = new TestInputFormatter();
// Act
var descriptor = new InputFormatterDescriptor(testFormatter);
// Assert
Assert.Same(testFormatter, descriptor.Instance);
Assert.Equal(testFormatter.GetType(), descriptor.OptionType);
}
private class TestInputFormatter : IInputFormatter
{
public bool CanRead(InputFormatterContext context)
{
throw new NotImplementedException();
}
public Task<object> ReadAsync(InputFormatterContext context)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -1,6 +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;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc.HeaderValueAbstractions;
using Microsoft.AspNet.Mvc.OptionDescriptors;
using Microsoft.AspNet.Testing;
using Xunit;
@ -21,5 +24,46 @@ namespace Microsoft.AspNet.Mvc.Core
// Act & Assert
ExceptionAssert.ThrowsArgument(() => new OutputFormatterDescriptor(type), "type", expected);
}
[Fact]
public void ConstructorSets_OutputFormatterType()
{
// Arrange
var type = typeof(TestOutputFormatter);
// Act
var descriptor = new OutputFormatterDescriptor(type);
// Assert
Assert.Equal(type, descriptor.OptionType);
Assert.Null(descriptor.Instance);
}
[Fact]
public void ConstructorSets_OutputFormatterInsnaceAndType()
{
// Arrange
var testFormatter = new TestOutputFormatter();
// Act
var descriptor = new OutputFormatterDescriptor(testFormatter);
// Assert
Assert.Same(testFormatter, descriptor.Instance);
Assert.Equal(testFormatter.GetType(), descriptor.OptionType);
}
private class TestOutputFormatter : IOutputFormatter
{
public bool CanWriteResult(OutputFormatterContext context, MediaTypeHeaderValue contentType)
{
throw new NotImplementedException();
}
public Task WriteAsync(OutputFormatterContext context)
{
throw new NotImplementedException();
}
}
}
}

View File

@ -37,8 +37,13 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
Assert.Equal(sampleInputInt.ToString(), await response.ReadBodyAsStringAsync());
}
[Fact]
public async Task JsonInputFormatter_IsSelectedForJsonRequest()
[Theory]
[InlineData("application/json")]
[InlineData("application/*")]
[InlineData("*/*")]
[InlineData("text/json")]
[InlineData("text/*")]
public async Task JsonInputFormatter_IsSelectedForJsonRequest(string requestContentType)
{
// Arrange
var server = TestServer.Create(_services, _app);
@ -47,13 +52,33 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
var input = "{\"SampleInt\":10}";
// Act
var response = await client.PostAsync("http://localhost/Home/Index", input, "application/json");
var response = await client.PostAsync("http://localhost/Home/Index", input, requestContentType);
//Assert
Assert.Equal(200, response.StatusCode);
Assert.Equal(sampleInputInt.ToString(), await response.ReadBodyAsStringAsync());
}
[Theory]
[InlineData("")]
[InlineData(null)]
[InlineData("invalid")]
public async Task JsonInputFormatter_IsNotSelectedForNonJsonRequests(string requestContentType)
{
// Arrange
var server = TestServer.Create(_services, _app);
var client = server.Handler;
var input = "{\"SampleInt\":10}";
// Act
var ex = await Assert.ThrowsAsync<InvalidOperationException>
(() => client.PostAsync("http://localhost/Home/CheckIfDummyIsNull", input, requestContentType));
//Assert
// TODO: Change the validation after https://github.com/aspnet/Mvc/issues/458 is fixed.
Assert.Equal("415: Unsupported content type " + requestContentType, ex.Message);
}
// TODO: By default XmlSerializerInputFormatter is called because of the order in which
// the formatters are registered. Add a test to call into DataContractSerializerInputFormatter.
}

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
null,
MediaTypeHeaderValueRange.AllMediaRange,
new Dictionary<string, string>(),
FormattingUtilities.Match,
HttpHeaderUtilitites.Match,
"*/*"
};
@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
"utf-8",
MediaTypeHeaderValueRange.SubtypeMediaRange,
new Dictionary<string, string>() { { "charset", "utf-8" }, { "foo", "bar" } },
FormattingUtilities.Match,
HttpHeaderUtilitites.Match,
"text/*;charset=utf-8;foo=bar",
};
@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
"utf-8",
MediaTypeHeaderValueRange.None,
new Dictionary<string, string>() { { "charset", "utf-8" }, { "foo", "bar" }, { "q", "0.0" } },
FormattingUtilities.NoMatch,
HttpHeaderUtilitites.NoMatch,
"text/plain;charset=utf-8;foo=bar;q=0.0",
};
}

View File

@ -8,9 +8,9 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions
public class StringWithQualityHeaderValueParsingTests
{
[Theory]
[InlineData("*", FormattingUtilities.Match, "*")]
[InlineData("*", HttpHeaderUtilitites.Match, "*")]
[InlineData("*", 0.7, "*;q=.7")]
[InlineData("iso-8859-5", FormattingUtilities.Match, "iso-8859-5")]
[InlineData("iso-8859-5", HttpHeaderUtilitites.Match, "iso-8859-5")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q=0.8")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q =0.8")]
[InlineData("unicode-1-1", 0.8, "unicode-1-1;q = 0.8")]

View File

@ -18,5 +18,11 @@ namespace FormatterWebSite.Controllers
{
return new DummyClass { SampleInt = sampleInput };
}
[HttpPost]
public bool CheckIfDummyIsNull([FromBody] DummyClass dummy)
{
return dummy != null;
}
}
}