From 64d797a48913eba9a8ea2926245bf548b23ce318 Mon Sep 17 00:00:00 2001 From: harshgMSFT Date: Fri, 8 Aug 2014 15:57:19 -0700 Subject: [PATCH] 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 --- .../ActionResults/ObjectResult.cs | 2 +- .../FilterActionInvoker.cs | 10 +- .../Formatters/DelegatingStream.cs | 2 +- .../Formatters/FormattingUtilities.cs | 3 +- .../Formatters/IInputFormatter.cs | 33 +++++ .../Formatters/IInputFormatterProvider.cs | 2 +- .../Formatters/InputFormatterContext.cs | 37 ++++++ .../InputFormatterProviderContext.cs | 9 +- .../Formatters/JsonInputFormatter.cs | 39 ++++-- ...MediaTypeWithQualityHeaderValueComparer.cs | 4 +- .../Formatters/OutputFormatter.cs | 2 +- .../StringWithQualityHeaderValueComparer.cs | 4 +- .../Formatters/TempInputFormatterProvider.cs | 23 +--- ...XmlDataContractSerializerInputFormatter.cs | 30 +++-- .../Formatters/XmlSerializerInputFormatter.cs | 30 +++-- src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs | 8 ++ .../Properties/Resources.Designer.cs | 16 +++ .../ReflectedActionInvoker.cs | 2 +- src/Microsoft.AspNet.Mvc.Core/Resources.resx | 3 + ...ngUtilities.cs => HttpHeaderUtilitites.cs} | 2 +- .../MediaTypeWithQualityHeaderValue.cs | 2 +- ...t.AspNet.Mvc.HeaderValueAbstractions.kproj | 5 - .../StringWithQualityHeaderValue.cs | 2 +- .../Formatters/IInputFormatter.cs | 29 ----- .../Formatters/InputFormatterContext.cs | 29 ----- .../Properties/Resources.Designer.cs | 16 --- .../Resources.resx | 3 - .../FormValueProviderFactory.cs | 4 +- .../Formatters/DelegatingStreamTests.cs | 2 +- .../Formatters/JsonInputFormatterTest.cs | 83 +++++++++---- ...taContractSerializerInputFormatterTests.cs | 109 ++++++++++------ .../XmlSerializerInputFormatterTests.cs | 117 ++++++++++++------ .../InputFormatterDescriptorTest.cs | 46 ++++++- .../OutputFormatterDescriptorTest.cs | 44 +++++++ .../InputFormatterTests.cs | 31 ++++- .../MediaTypeHeaderValueParsingTests.cs | 6 +- ...tringWithQualityHeaderValueParsingTests.cs | 4 +- .../Controllers/HomeController.cs | 6 + 38 files changed, 535 insertions(+), 264 deletions(-) rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/DelegatingStream.cs (99%) rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/FormattingUtilities.cs (94%) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatter.cs rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/IInputFormatterProvider.cs (91%) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterContext.cs rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/InputFormatterProviderContext.cs (71%) rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/JsonInputFormatter.cs (83%) rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/XmlDataContractSerializerInputFormatter.cs (81%) rename src/{Microsoft.AspNet.Mvc.ModelBinding => Microsoft.AspNet.Mvc.Core}/Formatters/XmlSerializerInputFormatter.cs (80%) rename src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/{FormattingUtilities.cs => HttpHeaderUtilitites.cs} (92%) delete mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs delete mode 100644 src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => Microsoft.AspNet.Mvc.Core.Test}/Formatters/DelegatingStreamTests.cs (94%) rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => Microsoft.AspNet.Mvc.Core.Test}/Formatters/JsonInputFormatterTest.cs (59%) rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => Microsoft.AspNet.Mvc.Core.Test}/Formatters/XmlDataContractSerializerInputFormatterTests.cs (75%) rename test/{Microsoft.AspNet.Mvc.ModelBinding.Test => Microsoft.AspNet.Mvc.Core.Test}/Formatters/XmlSerializerInputFormatterTests.cs (74%) diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs index 88573bf49d..3cbbc2524d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs @@ -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; diff --git a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs index 0c753ff0e3..f7215f0148 100644 --- a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/DelegatingStream.cs similarity index 99% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/DelegatingStream.cs index d807799f27..afc27c9430 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/DelegatingStream.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/DelegatingStream.cs @@ -6,7 +6,7 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -namespace Microsoft.AspNet.Mvc.ModelBinding +namespace Microsoft.AspNet.Mvc { /// /// Stream that delegates to an inner stream. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs similarity index 94% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs index d8da31d762..d8f7cb52c5 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/FormattingUtilities.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/FormattingUtilities.cs @@ -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 { /// /// Contains methods which are used by input formatters. diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatter.cs new file mode 100644 index 0000000000..2c69347ee9 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatter.cs @@ -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 +{ + /// + /// Reads an object from the request body. + /// + public interface IInputFormatter + { + /// + /// Determines whether this can de-serialize + /// an object of the specified type. + /// + /// Input formatter context associated with this call. + /// True if this supports the passed in + /// request's content-type and is able to de-serialize the request body. + /// False otherwise. + bool CanRead(InputFormatterContext context); + + /// + /// Called during deserialization to read an object from the request. + /// + /// Input formatter context associated with this call. + /// A task that deserializes the request body. + Task ReadAsync(InputFormatterContext context); + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatterProvider.cs similarity index 91% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatterProvider.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatterProvider.cs index 5b12e99a1f..f46073dc07 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatterProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/IInputFormatterProvider.cs @@ -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 { diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterContext.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterContext.cs new file mode 100644 index 0000000000..96a798ca86 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterContext.cs @@ -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 +{ + /// + /// Represents information used by an input formatter for + /// deserializing the request body into an object. + /// + public class InputFormatterContext + { + /// + /// Creates a new instance of . + /// + public InputFormatterContext([NotNull] ActionContext actionContext, + [NotNull] Type modelType) + { + ActionContext = actionContext; + ModelType = modelType; + } + + /// + /// Action context associated with the current call. + /// + public ActionContext ActionContext { get; private set; } + + /// + /// Represents the expected type of the model represented by the request body. + /// + public Type ModelType { get; private set; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterProviderContext.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterProviderContext.cs similarity index 71% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterProviderContext.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterProviderContext.cs index 90b105a9a3..68c4bf1ed7 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterProviderContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/InputFormatterProviderContext.cs @@ -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; } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs similarity index 83% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs index 9bcaaab953..6cdff8e15d 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/JsonInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/JsonInputFormatter.cs @@ -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 /// /// Gets or sets if deserialization errors are captured. When set, these errors appear in - /// the instance of . + /// the instance of . /// public bool CaptureDeserilizationErrors { get; set; } /// - 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)); + } + + /// + public async Task 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); } /// @@ -118,8 +133,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private Task 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)); } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/MediaTypeWithQualityHeaderValueComparer.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/MediaTypeWithQualityHeaderValueComparer.cs index 65ecd3a84d..047ab24b14 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/MediaTypeWithQualityHeaderValueComparer.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/MediaTypeWithQualityHeaderValueComparer.cs @@ -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) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs index ff42bb55b8..383117c109 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/OutputFormatter.cs @@ -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); diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/StringWithQualityHeaderValueComparer.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/StringWithQualityHeaderValueComparer.cs index 1f39c3962e..f1f9365c7f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/StringWithQualityHeaderValueComparer.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/StringWithQualityHeaderValueComparer.cs @@ -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) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs index 681e1675ed..e91321d963 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/TempInputFormatterProvider.cs @@ -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)); } } } diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerInputFormatter.cs similarity index 81% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerInputFormatter.cs index cf7751095b..29ce23cb7e 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlDataContractSerializerInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlDataContractSerializerInputFormatter.cs @@ -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 { /// /// This class handles deserialization of input XML data @@ -58,21 +59,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding get { return _readerQuotas; } } + /// + 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)); + } + /// /// Reads the input XML. /// /// The input formatter context which contains the body to be read. /// Task which reads the input. - public async Task ReadAsync(InputFormatterContext context) + public async Task 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); } /// @@ -107,8 +121,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private Task 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))) { diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlSerializerInputFormatter.cs b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerInputFormatter.cs similarity index 80% rename from src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlSerializerInputFormatter.cs rename to src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerInputFormatter.cs index 3013480a4d..dd910646a2 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/XmlSerializerInputFormatter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Formatters/XmlSerializerInputFormatter.cs @@ -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 { /// /// This class handles deserialization of input XML data @@ -58,21 +59,34 @@ namespace Microsoft.AspNet.Mvc.ModelBinding get { return _readerQuotas; } } + /// + 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)); + } + /// /// Reads the input XML. /// /// The input formatter context which contains the body to be read. /// Task which reads the input. - public async Task ReadAsync(InputFormatterContext context) + public async Task 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); } /// @@ -107,8 +121,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding private Task 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))) { diff --git a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs index ba1b511050..b317439e0f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/MvcOptions.cs @@ -51,8 +51,16 @@ namespace Microsoft.AspNet.Mvc } } + /// + /// Get a list of the which are used to construct + /// a list of by . + /// public List OutputFormatters { get; private set; } + /// + /// Get a list of the which are used to construct + /// a list of by . + /// public List InputFormatters { get; private set; } /// diff --git a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs index 1efeb0c0f4..99db9b5234 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Properties/Resources.Designer.cs @@ -1066,6 +1066,22 @@ namespace Microsoft.AspNet.Mvc.Core return string.Format(CultureInfo.CurrentCulture, GetString("OutputFormatterNoEncoding"), p0); } + /// + /// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. + /// + internal static string InputFormatterNoEncoding + { + get { return GetString("InputFormatterNoEncoding"); } + } + + /// + /// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. + /// + internal static string FormatInputFormatterNoEncoding(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("InputFormatterNoEncoding"), p0); + } + /// /// 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. /// diff --git a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs index 85a385d344..d1840e7958 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ReflectedActionInvoker.cs @@ -81,4 +81,4 @@ namespace Microsoft.AspNet.Mvc }; } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Resources.resx b/src/Microsoft.AspNet.Mvc.Core/Resources.resx index ef973ac845..8225d5379f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.Core/Resources.resx @@ -315,6 +315,9 @@ 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. + + No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. + 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. diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/HttpHeaderUtilitites.cs similarity index 92% rename from src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs rename to src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/HttpHeaderUtilitites.cs index f36bb61559..cdb145a1d2 100644 --- a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/FormattingUtilities.cs +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/HttpHeaderUtilitites.cs @@ -3,7 +3,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions { - public static class FormattingUtilities + public static class HttpHeaderUtilitites { /// /// Quality factor to indicate a perfect match. diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs index e6632d89f1..89fa195332 100644 --- a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/MediaTypeWithQualityHeaderValue.cs @@ -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)) { diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj index 7c7f516cc3..cf23e633b8 100644 --- a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/Microsoft.AspNet.Mvc.HeaderValueAbstractions.kproj @@ -16,10 +16,5 @@ 2.0 - - - ResXFileCodeGenerator - - \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs index 61621ddb9d..548b902a5e 100644 --- a/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs +++ b/src/Microsoft.AspNet.Mvc.HeaderValueAbstractions/StringWithQualityHeaderValue.cs @@ -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(); diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs deleted file mode 100644 index 8424e4fbe6..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/IInputFormatter.cs +++ /dev/null @@ -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 - { - /// - /// Gets the mutable collection of media types supported by this instance. - /// - IList SupportedMediaTypes { get; } - - /// - /// Gets the mutable collection of character encodings supported by this - /// instance. - /// - IList SupportedEncodings { get; } - - /// - /// Called during deserialization to read an object from the request. - /// - Task ReadAsync(InputFormatterContext context); - } -} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs deleted file mode 100644 index 823fe32b4f..0000000000 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Formatters/InputFormatterContext.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs index 72e9396357..8f6da0b7ee 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Properties/Resources.Designer.cs @@ -74,22 +74,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding return GetString("JQuerySyntaxMissingClosingBracket"); } - /// - /// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. - /// - internal static string MediaTypeFormatterNoEncoding - { - get { return GetString("MediaTypeFormatterNoEncoding"); } - } - - /// - /// No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. - /// - internal static string FormatMediaTypeFormatterNoEncoding(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("MediaTypeFormatterNoEncoding"), p0); - } - /// /// 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)]. /// diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx b/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx index 5bde6c1dbe..5f322e7d89 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/Resources.resx @@ -129,9 +129,6 @@ The key is invalid JQuery syntax because it is missing a closing bracket. - - No encoding found for input formatter '{0}'. There must be at least one supported encoding registered in order for the formatter to read content. - 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)]. diff --git a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/FormValueProviderFactory.cs b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/FormValueProviderFactory.cs index a276309c27..e18bed1260 100644 --- a/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/FormValueProviderFactory.cs +++ b/src/Microsoft.AspNet.Mvc.ModelBinding/ValueProviders/FormValueProviderFactory.cs @@ -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) diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/DelegatingStreamTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/DelegatingStreamTests.cs similarity index 94% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/DelegatingStreamTests.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/Formatters/DelegatingStreamTests.cs index d19295db4d..334fea6dd6 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/DelegatingStreamTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/DelegatingStreamTests.cs @@ -5,7 +5,7 @@ using System.IO; using Xunit; -namespace Microsoft.AspNet.Mvc.ModelBinding.Test +namespace Microsoft.AspNet.Mvc { public class DelegatingStreamTests { diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/JsonInputFormatterTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs similarity index 59% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/JsonInputFormatterTest.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs index be4a915022..87f2da86b5 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/JsonInputFormatterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/JsonInputFormatterTest.cs @@ -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(context.Model); - Assert.Equal("Person Name", model.Name); - Assert.Equal(30, model.Age); + var userModel = Assert.IsType(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(() => 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(); var headers = new Mock(); - 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.SetupGet(c => c.Request).Returns(request.Object); + httpContext.SetupGet(c => c.Request).Returns(request.Object); return httpContext.Object; } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs similarity index 75% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs index 110e69d33e..3094083fbe 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlDataContractSerializerInputFormatterTests.cs @@ -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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(context.Model); - var model = context.Model as DummyClass; - Assert.Equal(expectedInt, model.SampleInt); + Assert.NotNull(model); + Assert.IsType(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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(); var headers = new Mock(); - 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.SetupGet(c => c.Request).Returns(request.Object); + httpContext.SetupGet(c => c.Request).Returns(request.Object); return httpContext.Object; } } diff --git a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlSerializerInputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs similarity index 74% rename from test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlSerializerInputFormatterTests.cs rename to test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs index 4bc8b0a6e6..1ce7128da5 100644 --- a/test/Microsoft.AspNet.Mvc.ModelBinding.Test/Formatters/XmlSerializerInputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Formatters/XmlSerializerInputFormatterTests.cs @@ -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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(context.Model); - var model = context.Model as DummyClass; - Assert.Equal(expectedInt, model.SampleInt); + Assert.NotNull(model); + Assert.IsType(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(context.Model); + Assert.NotNull(model); + Assert.IsType(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(); var headers = new Mock(); - 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.SetupGet(c => c.Request).Returns(request.Object); + httpContext.SetupGet(c => c.Request).Returns(request.Object); return httpContext.Object; } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/InputFormatterDescriptorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/InputFormatterDescriptorTest.cs index 027a75d1f8..298b766a49 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/InputFormatterDescriptorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/InputFormatterDescriptorTest.cs @@ -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 ReadAsync(InputFormatterContext context) + { + throw new NotImplementedException(); + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/OutputFormatterDescriptorTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/OutputFormatterDescriptorTest.cs index 5b395b41cc..3d50106498 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/OutputFormatterDescriptorTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/OptionDescriptors/OutputFormatterDescriptorTest.cs @@ -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(); + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/InputFormatterTests.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/InputFormatterTests.cs index 6438b8ef91..559bfb87e0 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/InputFormatterTests.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/InputFormatterTests.cs @@ -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 + (() => 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. } diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs index 96779feb76..981e6b2af0 100644 --- a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/MediaTypeHeaderValueParsingTests.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions null, MediaTypeHeaderValueRange.AllMediaRange, new Dictionary(), - FormattingUtilities.Match, + HttpHeaderUtilitites.Match, "*/*" }; @@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Mvc.HeaderValueAbstractions "utf-8", MediaTypeHeaderValueRange.SubtypeMediaRange, new Dictionary() { { "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() { { "charset", "utf-8" }, { "foo", "bar" }, { "q", "0.0" } }, - FormattingUtilities.NoMatch, + HttpHeaderUtilitites.NoMatch, "text/plain;charset=utf-8;foo=bar;q=0.0", }; } diff --git a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs index 6992c48e32..fa0c884441 100644 --- a/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs +++ b/test/Microsoft.AspNet.Mvc.HeaderValueAbstractions.Test/StringWithQualityHeaderValueParsingTests.cs @@ -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")] diff --git a/test/WebSites/FormatterWebSite/Controllers/HomeController.cs b/test/WebSites/FormatterWebSite/Controllers/HomeController.cs index e8d8f0cdaf..a15ed3820f 100644 --- a/test/WebSites/FormatterWebSite/Controllers/HomeController.cs +++ b/test/WebSites/FormatterWebSite/Controllers/HomeController.cs @@ -18,5 +18,11 @@ namespace FormatterWebSite.Controllers { return new DummyClass { SampleInt = sampleInput }; } + + [HttpPost] + public bool CheckIfDummyIsNull([FromBody] DummyClass dummy) + { + return dummy != null; + } } } \ No newline at end of file