From be7dfa30af9d33010805cb54b470e8753f7c1fa5 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Thu, 3 Jan 2019 11:02:07 -0800 Subject: [PATCH] Use InputFormatterException for another case (#6316) - #4917 nits: - test `ExceptionPolicy` logic in an input formatter - remove old Mono workarounds in `SerializaterErrorTests` - fix typo in `SerializableErrorTests` (not using theory data) --- .../XmlSerializerInputFormatter.cs | 14 ++- .../Binders/BodyModelBinderTests.cs | 2 - .../SerializableErrorTests.cs | 109 ++++++++++++------ 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs index 729d9e1117..336cea13a7 100644 --- a/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs +++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.Formatters.Xml/XmlSerializerInputFormatter.cs @@ -159,9 +159,19 @@ namespace Microsoft.AspNetCore.Mvc.Formatters } // XmlSerializer wraps actual exceptions (like FormatException or XmlException) into an InvalidOperationException // https://github.com/dotnet/corefx/blob/master/src/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs#L652 - catch (InvalidOperationException exception) when (exception.InnerException is FormatException || exception.InnerException is XmlException) + catch (InvalidOperationException exception) when (exception.InnerException != null && + exception.InnerException.InnerException == null && + string.Equals("Microsoft.GeneratedCode", exception.InnerException.Source, StringComparison.InvariantCulture)) { - throw new InputFormatterException(Resources.ErrorDeserializingInputData, exception); + // Know this was an XML parsing error because the inner Exception was thrown in the (generated) + // assembly the XmlSerializer uses for parsing. The problem did not arise lower in the stack i.e. it's + // not (for example) an out-of-memory condition. + throw new InputFormatterException(Resources.ErrorDeserializingInputData, exception.InnerException); + } + catch (InvalidOperationException exception) when (exception.InnerException is FormatException || + exception.InnerException is XmlException) + { + throw new InputFormatterException(Resources.ErrorDeserializingInputData, exception.InnerException); } } diff --git a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Binders/BodyModelBinderTests.cs b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Binders/BodyModelBinderTests.cs index 0817b368ba..6567e08b45 100644 --- a/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Binders/BodyModelBinderTests.cs +++ b/src/Mvc/test/Microsoft.AspNetCore.Mvc.Core.Test/ModelBinding/Binders/BodyModelBinderTests.cs @@ -839,8 +839,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders _throwNonInputFormatterException = throwNonInputFormatterException; } - public override InputFormatterExceptionPolicy ExceptionPolicy => InputFormatterExceptionPolicy.AllExceptions; - public override Task ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding) { if (_throwNonInputFormatterException) diff --git a/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/SerializableErrorTests.cs b/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/SerializableErrorTests.cs index 7f073ee9a3..cf4220ab60 100644 --- a/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/SerializableErrorTests.cs +++ b/src/Mvc/test/Microsoft.AspNetCore.Mvc.FunctionalTests/SerializableErrorTests.cs @@ -7,8 +7,6 @@ using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Formatters.Xml; -using Microsoft.AspNetCore.Testing; -using Microsoft.AspNetCore.Testing.xunit; using Xunit; namespace Microsoft.AspNetCore.Mvc.FunctionalTests @@ -22,22 +20,15 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests public HttpClient Client { get; } - public static TheoryData AcceptHeadersData + public static TheoryData AcceptHeadersData { get { - var data = new TheoryData + return new TheoryData { + "application/xml-dcs", "application/xml-xmlser" }; - - // Mono issue - https://github.com/aspnet/External/issues/18 - if (!TestPlatformHelper.IsMono) - { - data.Add("application/xml-dcs"); - } - - return data; } } @@ -62,19 +53,16 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests XmlAssert.Equal(expectedXml, responseData); } - [ConditionalTheory] - // Mono issue - https://github.com/aspnet/External/issues/18 - // XmlSerializer test is disabled Mono.Xml2.XmlTextReader.ReadText is unable to read the XML. - // This is fixed in mono 4.3.0. - [FrameworkSkipCondition(RuntimeFrameworks.Mono)] - [InlineData("application/xml-xmlser")] - [InlineData("application/xml-dcs")] + [Theory] + [MemberData(nameof(AcceptHeadersData))] public async Task PostedSerializableError_IsBound(string acceptHeader) { // Arrange var expectedXml = "key1-errorThe input was not valid."; - var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/LogErrors"); - request.Content = new StringContent(expectedXml, Encoding.UTF8, acceptHeader); + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/LogErrors") + { + Content = new StringContent(expectedXml, Encoding.UTF8, acceptHeader) + }; request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(acceptHeader)); // Act @@ -89,25 +77,80 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests XmlAssert.Equal(expectedXml, responseData); } - [ConditionalTheory] - // Mono issue - https://github.com/aspnet/External/issues/18 - // XmlSerializer test is disabled Mono.Xml2.XmlTextReader.ReadText is unable to read the XML. - // This is fixed in mono 4.3.0. - [FrameworkSkipCondition(RuntimeFrameworks.Mono)] - [InlineData("application/xml-xmlser")] - [InlineData("application/xml-dcs")] - public async Task IsReturnedInExpectedFormat(string acceptHeader) + public static TheoryData InvalidInputAndHeadersData + { + get + { + return new TheoryData + { + { + "application/xml-dcs", + "" + + "" + + "2foo" + }, + { + "application/xml-xmlser", + "" + + "" + + "2foo" + }, + }; + } + } + + [Theory] + [MemberData(nameof(InvalidInputAndHeadersData))] + public async Task IsReturnedInExpectedFormat(string acceptHeader, string inputXml) { // Arrange - var input = "" + - "" + - "2foo"; var expected = "The field Id must be between 10 and 100." + "The field Name must be a string or array type with a minimum " + "length of '15'."; var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/CreateEmployee"); request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(acceptHeader)); - request.Content = new StringContent(input, Encoding.UTF8, "application/xml-dcs"); + request.Content = new StringContent(inputXml, Encoding.UTF8, acceptHeader); + + // Act + var response = await Client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); + var responseData = await response.Content.ReadAsStringAsync(); + XmlAssert.Equal(expected, responseData); + } + + public static TheoryData IncorrectTopLevelInputAndHeadersData + { + get + { + return new TheoryData + { + { + "application/xml-dcs", + "" + + "" + + "2foo" + }, + { + "application/xml-xmlser", + "" + + "" + + "2foo" + }, + }; + } + } + + [Theory] + [MemberData(nameof(IncorrectTopLevelInputAndHeadersData))] + public async Task IncorrectTopLevelElement_ReturnsExpectedError(string acceptHeader, string inputXml) + { + // Arrange + var expected = "An error occurred while deserializing input data."; + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/SerializableError/CreateEmployee"); + request.Headers.Accept.Add(MediaTypeWithQualityHeaderValue.Parse(acceptHeader)); + request.Content = new StringContent(inputXml, Encoding.UTF8, acceptHeader); // Act var response = await Client.SendAsync(request);