Logging for content-negotiation

This commit is contained in:
Kiran Challa 2015-04-13 10:43:13 -07:00
parent 7576116969
commit c3f10f4a0f
14 changed files with 74 additions and 3 deletions

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -11,6 +10,7 @@ using Microsoft.AspNet.Mvc.Core;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Internal; using Microsoft.Framework.Internal;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
@ -40,9 +40,12 @@ namespace Microsoft.AspNet.Mvc
public override async Task ExecuteResultAsync(ActionContext context) public override async Task ExecuteResultAsync(ActionContext context)
{ {
var logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<ObjectResult>>();
// See if the list of content types added to this object result is valid. // See if the list of content types added to this object result is valid.
ThrowIfUnsupportedContentType(); ThrowIfUnsupportedContentType();
var formatters = GetDefaultFormatters(context); var formatters = GetDefaultFormatters(context);
var formatterContext = new OutputFormatterContext() var formatterContext = new OutputFormatterContext()
{ {
DeclaredType = DeclaredType, DeclaredType = DeclaredType,
@ -55,10 +58,18 @@ namespace Microsoft.AspNet.Mvc
if (selectedFormatter == null) if (selectedFormatter == null)
{ {
// No formatter supports this. // No formatter supports this.
logger.LogWarning("No output formatter was found to write the response.");
context.HttpContext.Response.StatusCode = StatusCodes.Status406NotAcceptable; context.HttpContext.Response.StatusCode = StatusCodes.Status406NotAcceptable;
return; return;
} }
logger.LogVerbose(
"Selected output formatter '{OutputFormatter}'and content type " +
"'{ContentType}' to write the response.",
selectedFormatter.GetType().FullName,
formatterContext.SelectedContentType);
if (StatusCode.HasValue) if (StatusCode.HasValue)
{ {
context.HttpContext.Response.StatusCode = StatusCode.Value; context.HttpContext.Response.StatusCode = StatusCode.Value;
@ -72,10 +83,17 @@ namespace Microsoft.AspNet.Mvc
OutputFormatterContext formatterContext, OutputFormatterContext formatterContext,
IEnumerable<IOutputFormatter> formatters) IEnumerable<IOutputFormatter> formatters)
{ {
var logger = formatterContext.ActionContext.HttpContext.RequestServices
.GetRequiredService<ILogger<ObjectResult>>();
// Check if any content-type was explicitly set (for example, via ProducesAttribute // Check if any content-type was explicitly set (for example, via ProducesAttribute
// or Url path extension mapping). If yes, then ignore content-negotiation and use this content-type. // or Url path extension mapping). If yes, then ignore content-negotiation and use this content-type.
if (ContentTypes.Count == 1) if (ContentTypes.Count == 1)
{ {
logger.LogVerbose(
"Skipped content negotiation as content type '{ContentType}' is explicitly set for the response.",
ContentTypes[0]);
return SelectFormatterUsingAnyAcceptableContentType(formatterContext, return SelectFormatterUsingAnyAcceptableContentType(formatterContext,
formatters, formatters,
ContentTypes); ContentTypes);
@ -94,6 +112,8 @@ namespace Microsoft.AspNet.Mvc
out requestContentType); out requestContentType);
if (!sortedAcceptHeaderMediaTypes.Any() && requestContentType == null) if (!sortedAcceptHeaderMediaTypes.Any() && requestContentType == null)
{ {
logger.LogVerbose("No information found on request to perform content negotiation.");
return SelectFormatterBasedOnTypeMatch(formatterContext, formatters); return SelectFormatterBasedOnTypeMatch(formatterContext, formatters);
} }
@ -123,6 +143,8 @@ namespace Microsoft.AspNet.Mvc
// fallback on type based match. // fallback on type based match.
if (selectedFormatter == null) if (selectedFormatter == null)
{ {
logger.LogVerbose("Could not find an output formatter based on content negotiation.");
// Set this flag to indicate that content-negotiation has failed to let formatters decide // Set this flag to indicate that content-negotiation has failed to let formatters decide
// if they want to write the response or not. // if they want to write the response or not.
formatterContext.FailedContentNegotiation = true; formatterContext.FailedContentNegotiation = true;

View File

@ -12,6 +12,7 @@ using Microsoft.AspNet.Http.Core.Collections;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -101,6 +102,8 @@ namespace Microsoft.AspNet.Mvc
optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter());
services.Setup(p => p.GetService(typeof(IOptions<MvcOptions>))) services.Setup(p => p.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockContextAccessor = new Mock<IScopedInstance<ActionBindingContext>>(); var mockContextAccessor = new Mock<IScopedInstance<ActionBindingContext>>();
mockContextAccessor mockContextAccessor

View File

@ -11,6 +11,7 @@ using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -105,6 +106,8 @@ namespace Microsoft.AspNet.Mvc
optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter());
httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
httpContext.Setup(o => o.Response) httpContext.Setup(o => o.Response)
.Returns(response); .Returns(response);

View File

@ -9,6 +9,7 @@ using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -94,6 +95,9 @@ namespace Microsoft.AspNet.Mvc
httpContext httpContext
.Setup(p => p.RequestServices.GetService(typeof(IOptions<MvcOptions>))) .Setup(p => p.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
httpContext
.Setup(p => p.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>();
mockActionBindingContext mockActionBindingContext

View File

@ -12,6 +12,7 @@ using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -104,6 +105,8 @@ namespace Microsoft.AspNet.Mvc
.Returns(mockContextAccessor.Object); .Returns(mockContextAccessor.Object);
httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor()); return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor());
} }

View File

@ -13,10 +13,10 @@ using Microsoft.AspNet.Mvc.Xml;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Moq; using Moq;
using Newtonsoft.Json.Utilities;
using Xunit; using Xunit;
namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
@ -671,10 +671,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
var objectResult = new ObjectResult(new Person() { Name = "John" }); var objectResult = new ObjectResult(new Person() { Name = "John" });
objectResult.ContentTypes = contentTypes.Select(contentType => MediaTypeHeaderValue.Parse(contentType)) objectResult.ContentTypes = contentTypes.Select(contentType => MediaTypeHeaderValue.Parse(contentType))
.ToList(); .ToList();
var actionContext = CreateMockActionContext();
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>( var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => objectResult.ExecuteResultAsync(null)); () => objectResult.ExecuteResultAsync(actionContext));
var expectedMessage = string.Format("The content-type '{0}' added in the 'ContentTypes' property is " + var expectedMessage = string.Format("The content-type '{0}' added in the 'ContentTypes' property is " +
"invalid. Media types which match all types or match all subtypes are not supported.", "invalid. Media types which match all types or match all subtypes are not supported.",
@ -896,6 +897,8 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults
optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader; optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader;
httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>))) httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor); .Returns(optionsAccessor);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>(); var mockActionBindingContext = new Mock<IScopedInstance<ActionBindingContext>>();

View File

@ -14,6 +14,8 @@ using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Mvc.ModelBinding.Validation;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.Testing; using Microsoft.AspNet.Testing;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Logging.Testing;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -1995,6 +1997,8 @@ namespace Microsoft.AspNet.Mvc
httpContext.SetupGet(c => c.Response).Returns(httpResponse); httpContext.SetupGet(c => c.Response).Returns(httpResponse);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary))) httpContext.Setup(o => o.RequestServices.GetService(typeof(ITempDataDictionary)))
.Returns(tempData); .Returns(tempData);
httpContext.Setup(o => o.RequestServices.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
httpResponse.Body = new MemoryStream(); httpResponse.Body = new MemoryStream();
var options = new MvcOptions(); var options = new MvcOptions();
@ -2078,6 +2082,8 @@ namespace Microsoft.AspNet.Mvc
.Returns(new Dictionary<object, object>()); .Returns(new Dictionary<object, object>());
context.Setup(c => c.RequestServices.GetService(typeof(ITempDataDictionary))) context.Setup(c => c.RequestServices.GetService(typeof(ITempDataDictionary)))
.Returns(new Mock<ITempDataDictionary>().Object); .Returns(new Mock<ITempDataDictionary>().Object);
context.Setup(c => c.RequestServices.GetService(typeof(ILoggerFactory)))
.Returns(new NullLoggerFactory());
var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor); var actionContext = new ActionContext(context.Object, new RouteData(), actionDescriptor);

View File

@ -15,6 +15,7 @@ using Microsoft.Framework.OptionsModel;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using Moq; using Moq;
using Xunit; using Xunit;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Mvc namespace Microsoft.AspNet.Mvc
{ {
@ -200,6 +201,8 @@ namespace Microsoft.AspNet.Mvc
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
// This is the ultimate fallback, it will be used if none of the formatters from options // This is the ultimate fallback, it will be used if none of the formatters from options
// work. // work.

View File

@ -10,6 +10,8 @@ using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Logging.Testing;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -86,6 +88,9 @@ namespace System.Web.Http
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return services.Object; return services.Object;
} }
} }

View File

@ -10,6 +10,7 @@ using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -86,6 +87,8 @@ namespace System.Web.Http
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return services.Object; return services.Object;
} }

View File

@ -11,6 +11,8 @@ using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Logging.Testing;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -100,6 +102,9 @@ namespace System.Web.Http
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return services.Object; return services.Object;
} }
} }

View File

@ -10,6 +10,8 @@ using Microsoft.AspNet.Http.Core;
using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Logging.Testing;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -87,6 +89,9 @@ namespace System.Web.Http
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return services.Object; return services.Object;
} }

View File

@ -9,6 +9,8 @@ using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Routing; using Microsoft.AspNet.Routing;
using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Logging.Testing;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
using Moq; using Moq;
using Xunit; using Xunit;
@ -60,6 +62,9 @@ namespace System.Web.Http
services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>))) services.Setup(s => s.GetService(typeof(IOptions<MvcOptions>)))
.Returns(optionsAccessor.Object); .Returns(optionsAccessor.Object);
services.Setup(s => s.GetService(typeof(ILogger<ObjectResult>)))
.Returns(new Mock<ILogger<ObjectResult>>().Object);
return services.Object; return services.Object;
} }

View File

@ -5,6 +5,7 @@
"dependencies": { "dependencies": {
"Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*", "Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-*",
"Microsoft.AspNet.Testing": "1.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*",
"Microsoft.Framework.Logging.Testing": "1.0.0-*",
"Moq": "4.2.1312.1622", "Moq": "4.2.1312.1622",
"xunit.runner.aspnet": "2.0.0-aspnet-*" "xunit.runner.aspnet": "2.0.0-aspnet-*"
}, },