// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net.Http.Formatting.Mocks; using System.Net.Http.Headers; using System.Text; using Microsoft.TestCommon; using Newtonsoft.Json.Linq; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace System.Net.Http.Formatting { public class DefaultContentNegotiatorTests { private readonly DefaultContentNegotiator _negotiator = new DefaultContentNegotiator(); private readonly HttpRequestMessage _request = new HttpRequestMessage(); [Fact] public void TypeIsCorrect() { Assert.Type.HasProperties(typeof(DefaultContentNegotiator), TypeAssert.TypeProperties.IsPublicVisibleClass); } [Fact] public void Negotiate_WhenTypeParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(null, _request, Enumerable.Empty()), "type"); } [Fact] public void Negotiate_WhenRequestParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(typeof(string), null, Enumerable.Empty()), "request"); } [Fact] public void Negotiate_WhenFormattersParameterIsNull_ThrowsException() { Assert.ThrowsArgumentNull(() => _negotiator.Negotiate(typeof(string), _request, null), "formatters"); } [Fact] public void Negotiate_ForEmptyFormatterCollection_ReturnsNull() { var result = _negotiator.Negotiate(typeof(string), _request, Enumerable.Empty()); Assert.Null(result); } [Fact] public void MediaTypeMappingTakesPrecedenceOverAcceptHeader() { // Prepare the request message _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); _request.Headers.Add("Browser", "IE"); _request.Headers.Add("Cookie", "ABC"); // Prepare the formatters List formatters = new List(); formatters.Add(new JsonMediaTypeFormatter()); formatters.Add(new XmlMediaTypeFormatter()); PlainTextFormatter frmtr = new PlainTextFormatter(); frmtr.SupportedMediaTypes.Clear(); frmtr.MediaTypeMappings.Clear(); frmtr.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/xml")); frmtr.MediaTypeMappings.Add(new MyMediaTypeMapping(new MediaTypeHeaderValue(("application/xml")))); formatters.Add(frmtr); // Act var result = _negotiator.Negotiate(typeof(string), _request, formatters); // Assert Assert.NotNull(result); Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_ForRequestReturnsFirstMatchingFormatter() { MediaTypeHeaderValue mediaType = new MediaTypeHeaderValue("application/myMediaType"); MediaTypeFormatter formatter1 = new MockMediaTypeFormatter() { CanWriteTypeCallback = (Type t) => false }; MediaTypeFormatter formatter2 = new MockMediaTypeFormatter() { CanWriteTypeCallback = (Type t) => true }; formatter2.SupportedMediaTypes.Add(mediaType); MediaTypeFormatterCollection collection = new MediaTypeFormatterCollection( new MediaTypeFormatter[] { formatter1, formatter2 }); _request.Content = new StringContent("test", Encoding.Default, mediaType.MediaType); var result = _negotiator.Negotiate(typeof(string), _request, collection); Assert.Same(formatter2, result.Formatter); Assert.MediaType.AreEqual(mediaType, result.MediaType, "Expected the formatter's media type to be returned."); } [Fact] public void Negotiate_SelectsJsonAsDefaultFormatter() { // Arrange _request.Content = new StringContent("test"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.IsType(result.Formatter); Assert.Equal(MediaTypeConstants.ApplicationJsonMediaType.MediaType, result.MediaType.MediaType); } [Fact] public void Negotiate_SelectsXmlFormatter_ForXhrRequestThatAcceptsXml() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXhrRequestThatDoesNotSpecifyAcceptHeaders() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_RespectsFormatterOrdering_ForXhrRequestThatDoesNotSpecifyAcceptHeaders() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); MediaTypeFormatterCollection formatters = new MediaTypeFormatterCollection(new MediaTypeFormatter[] { new XmlMediaTypeFormatter(), new JsonMediaTypeFormatter(), new FormUrlEncodedMediaTypeFormatter() }); // Act var result = _negotiator.Negotiate(typeof(string), _request, formatters); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXHRAndJsonValueResponse() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); // Act var result = _negotiator.Negotiate(typeof(JToken), _request, new MediaTypeFormatterCollection()); Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_SelectsJsonFormatter_ForXHRAndMatchAllAcceptHeader() { // Accept _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); _request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("*/*")); // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/json", result.MediaType.MediaType); Assert.IsType(result.Formatter); } [Fact] public void Negotiate_UsesRequestedFormatterForXHRAndMatchAllPlusOtherAcceptHeader() { // Arrange _request.Content = new StringContent("test"); _request.Headers.Add("x-requested-with", "XMLHttpRequest"); _request.Headers.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); // XHR header sent by Firefox 3b5 // Act var result = _negotiator.Negotiate(typeof(string), _request, new MediaTypeFormatterCollection()); // Assert Assert.Equal("application/xml", result.MediaType.MediaType); Assert.IsType(result.Formatter); } private class PlainTextFormatter : MediaTypeFormatter { public override bool CanReadType(Type type) { return true; } public override bool CanWriteType(Type type) { return true; } } private class MyMediaTypeMapping : MediaTypeMapping { public MyMediaTypeMapping(MediaTypeHeaderValue mediaType) : base(mediaType) { } public override double TryMatchMediaType(HttpRequestMessage request) { if (request.Headers.Contains("Cookie")) { return 1.0; } else { return 0; } } } } }