[Fixes 7139] Add format filter mapping for xml formatters

This commit is contained in:
Kiran Challa 2018-01-08 12:13:04 -08:00
parent 00c6b53b06
commit 2aae8774f6
7 changed files with 160 additions and 20 deletions

View File

@ -80,7 +80,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
options,
_jsonOptions));
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValues.ApplicationJson);
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(IJsonPatchDocument)));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(JToken)));

View File

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
{
@ -42,6 +43,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter(_loggerFactory));
options.InputFormatters.Add(new XmlDataContractSerializerInputFormatter(options));
// Do not override any user mapping
var key = "xml";
var mapping = options.FormatterMappings.GetMediaTypeMappingForFormat(key);
if (string.IsNullOrEmpty(mapping))
{
options.FormatterMappings.SetMediaTypeMappingForFormat(
key,
MediaTypeHeaderValues.ApplicationXml);
}
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider("System.Xml.Linq.XObject"));
options.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider("System.Xml.XmlNode"));
}

View File

@ -4,6 +4,7 @@
using System;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
{
@ -35,6 +36,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
/// <param name="options">The <see cref="MvcOptions"/>.</param>
public void Configure(MvcOptions options)
{
// Do not override any user mapping
var key = "xml";
var mapping = options.FormatterMappings.GetMediaTypeMappingForFormat(key);
if (string.IsNullOrEmpty(mapping))
{
options.FormatterMappings.SetMediaTypeMappingForFormat(
key,
MediaTypeHeaderValues.ApplicationXml);
}
options.OutputFormatters.Add(new XmlSerializerOutputFormatter(_loggerFactory));
options.InputFormatters.Add(new XmlSerializerInputFormatter(options));
}

View File

@ -0,0 +1,42 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
{
public class MvcXmlDataContractSerializerMvcOptionsSetupTest
{
[Fact]
public void AddsFormatterMapping()
{
// Arrange
var optionsSetup = new MvcXmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions();
// Act
optionsSetup.Configure(options);
// Assert
var mappedContentType = options.FormatterMappings.GetMediaTypeMappingForFormat("xml");
Assert.Equal("application/xml", mappedContentType);
}
[Fact]
public void DoesNotOverrideExistingMapping()
{
// Arrange
var optionsSetup = new MvcXmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions();
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml");
// Act
optionsSetup.Configure(options);
// Assert
var mappedContentType = options.FormatterMappings.GetMediaTypeMappingForFormat("xml");
Assert.Equal("text/xml", mappedContentType);
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
{
public class MvcXmlSerializerMvcOptionsSetupTest
{
[Fact]
public void AddsFormatterMapping()
{
// Arrange
var optionsSetup = new MvcXmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions();
// Act
optionsSetup.Configure(options);
// Assert
var mappedContentType = options.FormatterMappings.GetMediaTypeMappingForFormat("xml");
Assert.Equal("application/xml", mappedContentType);
}
[Fact]
public void DoesNotOverrideExistingMapping()
{
// Arrange
var optionsSetup = new MvcXmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions();
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml");
// Act
optionsSetup.Configure(options);
// Assert
var mappedContentType = options.FormatterMappings.GetMediaTypeMappingForFormat("xml");
Assert.Equal("text/xml", mappedContentType);
}
}
}

View File

@ -450,13 +450,12 @@ END:VCARD
[Fact]
public async Task ProducesAttribute_And_FormatFilterAttribute_Conflicting()
{
// Arrange
var expectedContentType = MediaTypeHeaderValue.Parse("application/json");
// Act
var response = await Client.GetAsync("http://localhost/FormatFilter/MethodWithFormatFilter.json");
// Arrange & Act
var response = await Client.GetAsync(
"http://localhost/FormatFilter/ProducesTakesPrecedenceOverUserSuppliedFormatMethod?format=json");
// Assert
// Explicit content type set by the developer takes precedence over the format requested by the end user
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
@ -464,7 +463,8 @@ END:VCARD
public async Task ProducesAttribute_And_FormatFilterAttribute_Collaborating()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/FormatFilter/MethodWithFormatFilter");
var response = await Client.GetAsync(
"http://localhost/FormatFilter/ProducesTakesPrecedenceOverUserSuppliedFormatMethod");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
@ -508,5 +508,24 @@ END:VCARD
var contact = xmlDeserializer.ReadObject(bodyStream) as Contact;
Assert.Equal("Jason Ecsemelle", contact.Name);
}
[Fact]
public async Task FormatFilter_XmlAsFormat_ReturnsXml()
{
// Arrange
var expectedBody = "<FormatFilterController.Customer xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\""
+ " xmlns=\"http://schemas.datacontract.org/2004/07/BasicWebSite.Controllers.ContentNegotiation\">"
+ "<Name>John</Name></FormatFilterController.Customer>";
// Act
var response = await Client.GetAsync(
"http://localhost/FormatFilter/CustomerInfo?format=xml");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("application/xml; charset=utf-8", response.Content.Headers.ContentType.ToString());
var body = await response.Content.ReadAsStringAsync();
Assert.Equal(expectedBody, body);
}
}
}

View File

@ -7,24 +7,39 @@ using Microsoft.AspNetCore.Mvc.Filters;
namespace BasicWebSite.Controllers.ContentNegotiation
{
[Produces("application/FormatFilterController")]
public class FormatFilterController : Controller
{
public override void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result as ObjectResult;
if (result != null)
{
result.Formatters.Add(new CustomFormatter("application/FormatFilterController"));
}
base.OnActionExecuted(context);
}
[Produces("application/FormatFilterController")]
[FormatFilter]
public string MethodWithFormatFilter()
[CustomFormatterActionFilter]
public string ProducesTakesPrecedenceOverUserSuppliedFormatMethod()
{
return "MethodWithFormatFilter";
}
[HttpGet]
[FormatFilter]
public Customer CustomerInfo()
{
return new Customer() { Name = "John" };
}
private class CustomFormatterActionFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result as ObjectResult;
if (result != null)
{
result.Formatters.Add(new CustomFormatter("application/FormatFilterController"));
}
}
}
public class Customer
{
public string Name { get; set; }
}
}
}