From 58a5ad22797f5dc3589a341832715dc188b0b3e9 Mon Sep 17 00:00:00 2001 From: Harsh Gupta Date: Tue, 7 Apr 2015 21:21:17 -0700 Subject: [PATCH] Adding Outputformatter in resource filters. --- .../ActionResults/ObjectResult.cs | 23 +++++-- .../ControllerActionInvoker.cs | 6 +- .../ControllerActionInvokerProvider.cs | 3 + .../FilterActionInvoker.cs | 5 ++ .../Filters/ResourceExecutingContext.cs | 5 ++ .../ParameterBinding/ActionBindingContext.cs | 2 + .../CreatedAtActionResultTests.cs | 10 +++ .../CreatedAtRouteResultTests.cs | 10 +++ .../ActionResults/CreatedResultTests.cs | 13 +++- .../HttpNotFoundObjectResultTest.cs | 10 +++ .../ActionResults/ObjectResultTests.cs | 58 +++++++++++++--- .../ControllerActionInvokerTest.cs | 7 +- .../FiltersTest.cs | 69 ++++++++++++++++++- .../BadRequestErrorMessageResultTest.cs | 10 +++ .../ActionResults/ExceptionResultTest.cs | 10 +++ .../InvalidModelStateResultTest.cs | 11 +++ .../NegotiatedContentResultTest.cs | 10 +++ .../OkNegotiatedContentResultTest.cs | 10 +++ .../Controllers/JsonOnlyController.cs | 5 ++ .../Controllers/ResourceFilterController.cs | 65 +++++++++++++++++ 20 files changed, 320 insertions(+), 22 deletions(-) create mode 100644 test/WebSites/FiltersWebSite/Controllers/ResourceFilterController.cs diff --git a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs index 135f94161d..bf8c69ccc9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ActionResults/ObjectResult.cs @@ -273,12 +273,27 @@ namespace Microsoft.AspNet.Mvc IEnumerable formatters = null; if (Formatters == null || Formatters.Count == 0) { - formatters = context + var actionBindingContext = context .HttpContext .RequestServices - .GetRequiredService>() - .Options - .OutputFormatters; + .GetRequiredService>() + .Value; + + // In scenarios where there is a resource filter which directly shortcircuits using an ObjectResult. + // actionBindingContext is not setup yet and is null. + if (actionBindingContext == null) + { + var options = context + .HttpContext + .RequestServices + .GetRequiredService>() + .Options; + formatters = options.OutputFormatters; + } + else + { + formatters = actionBindingContext.OutputFormatters ?? new List(); + } } else { diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs index efa26b64b4..1031623b04 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvoker.cs @@ -24,6 +24,7 @@ namespace Microsoft.AspNet.Mvc.Core [NotNull] IControllerFactory controllerFactory, [NotNull] ControllerActionDescriptor descriptor, [NotNull] IReadOnlyList inputFormatters, + [NotNull] IReadOnlyList outputFormatters, [NotNull] IControllerActionArgumentBinder controllerActionArgumentBinder, [NotNull] IReadOnlyList modelBinders, [NotNull] IReadOnlyList modelValidatorProviders, @@ -31,9 +32,10 @@ namespace Microsoft.AspNet.Mvc.Core [NotNull] IScopedInstance actionBindingContextAccessor, [NotNull] ITempDataDictionary tempData) : base( - actionContext, + actionContext, filterProviders, - inputFormatters, + inputFormatters, + outputFormatters, modelBinders, modelValidatorProviders, valueProviderFactories, diff --git a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs index d99a8881ff..598acc1972 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ControllerActionInvokerProvider.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Mvc.Core private readonly IFilterProvider[] _filterProviders; private readonly IReadOnlyList _inputFormatters; private readonly IReadOnlyList _modelBinders; + private readonly IReadOnlyList _outputFormatters; private readonly IReadOnlyList _modelValidatorProviders; private readonly IReadOnlyList _valueProviderFactories; private readonly IScopedInstance _actionBindingContextAccessor; @@ -34,6 +35,7 @@ namespace Microsoft.AspNet.Mvc.Core _filterProviders = filterProviders.OrderBy(item => item.Order).ToArray(); _argumentBinder = argumentBinder; _inputFormatters = optionsAccessor.Options.InputFormatters.ToArray(); + _outputFormatters = optionsAccessor.Options.OutputFormatters.ToArray(); _modelBinders = optionsAccessor.Options.ModelBinders.ToArray(); _modelValidatorProviders = optionsAccessor.Options.ModelValidatorProviders.ToArray(); _valueProviderFactories = optionsAccessor.Options.ValueProviderFactories.ToArray(); @@ -59,6 +61,7 @@ namespace Microsoft.AspNet.Mvc.Core _controllerFactory, actionDescriptor, _inputFormatters, + _outputFormatters, _argumentBinder, _modelBinders, _modelValidatorProviders, diff --git a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs index 1f01eb1d14..ec65bd2f57 100644 --- a/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs +++ b/src/Microsoft.AspNet.Mvc.Core/FilterActionInvoker.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Mvc.Core private readonly IReadOnlyList _filterProviders; private readonly IReadOnlyList _inputFormatters; private readonly IReadOnlyList _modelBinders; + private readonly IReadOnlyList _outputFormatters; private readonly IReadOnlyList _modelValidatorProviders; private readonly IReadOnlyList _valueProviderFactories; @@ -43,6 +44,7 @@ namespace Microsoft.AspNet.Mvc.Core [NotNull] ActionContext actionContext, [NotNull] IReadOnlyList filterProviders, [NotNull] IReadOnlyList inputFormatters, + [NotNull] IReadOnlyList outputFormatters, [NotNull] IReadOnlyList modelBinders, [NotNull] IReadOnlyList modelValidatorProviders, [NotNull] IReadOnlyList valueProviderFactories, @@ -52,6 +54,7 @@ namespace Microsoft.AspNet.Mvc.Core _filterProviders = filterProviders; _inputFormatters = inputFormatters; + _outputFormatters = outputFormatters; _modelBinders = modelBinders; _modelValidatorProviders = modelValidatorProviders; _valueProviderFactories = valueProviderFactories; @@ -208,6 +211,7 @@ namespace Microsoft.AspNet.Mvc.Core var context = new ResourceExecutingContext(ActionContext, _filters); context.InputFormatters = new List(_inputFormatters); + context.OutputFormatters = new List(_outputFormatters); context.ModelBinders = new List(_modelBinders); context.ValidatorProviders = new List(_modelValidatorProviders); context.ValueProviderFactories = new List(_valueProviderFactories); @@ -417,6 +421,7 @@ namespace Microsoft.AspNet.Mvc.Core ActionBindingContext = new ActionBindingContext(); ActionBindingContext.InputFormatters = _resourceExecutingContext.InputFormatters; + ActionBindingContext.OutputFormatters = _resourceExecutingContext.OutputFormatters; ActionBindingContext.ModelBinder = new CompositeModelBinder(_resourceExecutingContext.ModelBinders); ActionBindingContext.ValidatorProvider = new CompositeModelValidatorProvider( _resourceExecutingContext.ValidatorProviders); diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs index 0350f78566..ebe4070ff9 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ResourceExecutingContext.cs @@ -28,6 +28,11 @@ namespace Microsoft.AspNet.Mvc /// public virtual IList InputFormatters { get; set; } + /// + /// Gets or sets the list of instances used to format the response. + /// + public virtual IList OutputFormatters { get; set; } + /// /// Gets or sets the list of instances used by model binding. /// diff --git a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs index 925f9f5e07..2b8595db75 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ParameterBinding/ActionBindingContext.cs @@ -15,6 +15,8 @@ namespace Microsoft.AspNet.Mvc public IList InputFormatters { get; set; } + public IList OutputFormatters { get; set; } + public IModelValidatorProvider ValidatorProvider { get; set; } } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs index 26ba66f262..5bdd667774 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtActionResultTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core; @@ -91,6 +92,7 @@ namespace Microsoft.AspNet.Mvc var httpContext = new DefaultHttpContext(); httpContext.Request.PathBase = new PathString(""); httpContext.Response.Body = new MemoryStream(); + var services = new Mock(); httpContext.RequestServices = services.Object; @@ -100,6 +102,14 @@ namespace Microsoft.AspNet.Mvc services.Setup(p => p.GetService(typeof(IOptions))) .Returns(optionsAccessor); + var mockContextAccessor = new Mock>(); + mockContextAccessor + .SetupGet(o => o.Value) + .Returns(new ActionBindingContext() { OutputFormatters = optionsAccessor.Options.OutputFormatters }); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockContextAccessor.Object); + return httpContext; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs index b3362aab46..5085ac5b98 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedAtRouteResultTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core; @@ -107,6 +108,15 @@ namespace Microsoft.AspNet.Mvc httpContext.Setup(o => o.Response) .Returns(response); + + var mockContextAccessor = new Mock>(); + mockContextAccessor + .SetupGet(o => o.Value) + .Returns((ActionBindingContext)null); + + httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance))) + .Returns(mockContextAccessor.Object); + return httpContext.Object; } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs index 9e990914b7..bcdb0da0c3 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/CreatedResultTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core; @@ -90,8 +91,18 @@ namespace Microsoft.AspNet.Mvc var optionsAccessor = new MockMvcOptionsAccessor(); optionsAccessor.Options.OutputFormatters.Add(new StringOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter()); - httpContext.Setup(p => p.RequestServices.GetService(typeof(IOptions))) + httpContext + .Setup(p => p.RequestServices.GetService(typeof(IOptions))) .Returns(optionsAccessor); + + var mockActionBindingContext = new Mock>(); + mockActionBindingContext + .SetupGet(o=> o.Value) + .Returns(new ActionBindingContext() { OutputFormatters = optionsAccessor.Options.OutputFormatters }); + httpContext + .Setup(o => o.RequestServices.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + return httpContext.Object; } } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/HttpNotFoundObjectResultTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/HttpNotFoundObjectResultTest.cs index 408f854346..c3dcff7aa8 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/HttpNotFoundObjectResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/HttpNotFoundObjectResultTest.cs @@ -92,6 +92,16 @@ namespace Microsoft.AspNet.Mvc optionsAccessor.Options.OutputFormatters.Add(new StringOutputFormatter()); optionsAccessor.Options.OutputFormatters.Add(new JsonOutputFormatter()); optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader; + var mockContextAccessor = new Mock>(); + mockContextAccessor + .SetupGet(o => o.Value) + .Returns(new ActionBindingContext() + { + OutputFormatters = optionsAccessor.Options.OutputFormatters + }); + + httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance))) + .Returns(mockContextAccessor.Object); httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions))) .Returns(optionsAccessor); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs index 9cf0745044..d8055bc01e 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ActionResults/ObjectResultTests.cs @@ -162,6 +162,25 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults Assert.Equal(expectedContentType, httpResponse.ContentType); } + [Fact] + public async Task ObjectResult_FallsBackOn_FormattersInOptions() + { + // Arrange + var formatter = GetMockFormatter(); + var actionContext = CreateMockActionContext( + new[] { formatter.Object }, + setupActionBindingContext: false); + + // Set the content type property explicitly to a single value. + var result = new ObjectResult("someValue"); + + // Act + await result.ExecuteResultAsync(actionContext); + + // Assert + formatter.Verify(o => o.WriteAsync(It.IsAny())); + } + [Fact] public async Task ObjectResult_WithSingleContentType_TheContentTypeIsIgnoredIfTheTypeIsString() { @@ -824,11 +843,11 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults } private static ActionContext CreateMockActionContext( - HttpResponse response = null, - string requestAcceptHeader = "application/*", - string requestContentType = "application/json", - string requestAcceptCharsetHeader = "", - bool respectBrowserAcceptHeader = false) + HttpResponse response = null, + string requestAcceptHeader = "application/*", + string requestContentType = "application/json", + string requestAcceptCharsetHeader = "", + bool respectBrowserAcceptHeader = false) { var formatters = new IOutputFormatter[] { new StringOutputFormatter(), new JsonOutputFormatter() }; @@ -842,12 +861,13 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults } private static ActionContext CreateMockActionContext( - IEnumerable outputFormatters, - HttpResponse response = null, - string requestAcceptHeader = "application/*", - string requestContentType = "application/json", - string requestAcceptCharsetHeader = "", - bool respectBrowserAcceptHeader = false) + IEnumerable outputFormatters, + HttpResponse response = null, + string requestAcceptHeader = "application/*", + string requestContentType = "application/json", + string requestAcceptCharsetHeader = "", + bool respectBrowserAcceptHeader = false, + bool setupActionBindingContext = true) { var httpContext = new Mock(); if (response != null) @@ -872,10 +892,26 @@ namespace Microsoft.AspNet.Mvc.Core.Test.ActionResults { optionsAccessor.Options.OutputFormatters.Add(formatter); } + optionsAccessor.Options.RespectBrowserAcceptHeader = respectBrowserAcceptHeader; httpContext.Setup(o => o.RequestServices.GetService(typeof(IOptions))) .Returns(optionsAccessor); + var mockActionBindingContext = new Mock>(); + + ActionBindingContext bindingContext = null; + if (setupActionBindingContext) + { + bindingContext = new ActionBindingContext { OutputFormatters = outputFormatters.ToList() }; + } + + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + httpContext.Setup(o => o.RequestServices.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + return new ActionContext(httpContext.Object, new RouteData(), new ActionDescriptor()); } diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs index 90635e5f4b..4b87de8c8a 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ControllerActionInvokerTest.cs @@ -11,7 +11,6 @@ using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.ModelBinding; -using Microsoft.AspNet.Mvc.ModelBinding.Metadata; using Microsoft.AspNet.Mvc.ModelBinding.Validation; using Microsoft.AspNet.Routing; using Microsoft.AspNet.Testing; @@ -2030,13 +2029,13 @@ namespace Microsoft.AspNet.Mvc filterProvider.SetupGet(fp => fp.Order) .Returns(DefaultOrder.DefaultFrameworkSortOrder); - var invoker = new TestControllerActionInvoker( actionContext, new[] { filterProvider.Object }, new MockControllerFactory(this), actionDescriptor, new InputFormatter[0], + new IOutputFormatter[0], Mock.Of(), new IModelBinder[0], new IModelValidatorProvider[0], @@ -2085,6 +2084,7 @@ namespace Microsoft.AspNet.Mvc var controllerFactory = new Mock(); controllerFactory.Setup(c => c.CreateController(It.IsAny())) .Returns(new TestController()); + var metadataProvider = new EmptyModelMetadataProvider(); var invoker = new ControllerActionInvoker( actionContext, @@ -2092,6 +2092,7 @@ namespace Microsoft.AspNet.Mvc controllerFactory.Object, actionDescriptor, new IInputFormatter[0], + new IOutputFormatter[0], new DefaultControllerActionArgumentBinder( metadataProvider, new DefaultObjectValidator(new IExcludeTypeValidationFilter[0], metadataProvider), @@ -2193,6 +2194,7 @@ namespace Microsoft.AspNet.Mvc MockControllerFactory controllerFactory, ControllerActionDescriptor descriptor, IReadOnlyList inputFormatters, + IReadOnlyList outputFormatters, IControllerActionArgumentBinder controllerActionArgumentBinder, IReadOnlyList modelBinders, IReadOnlyList modelValidatorProviders, @@ -2205,6 +2207,7 @@ namespace Microsoft.AspNet.Mvc controllerFactory, descriptor, inputFormatters, + outputFormatters, controllerActionArgumentBinder, modelBinders, modelValidatorProviders, diff --git a/test/Microsoft.AspNet.Mvc.FunctionalTests/FiltersTest.cs b/test/Microsoft.AspNet.Mvc.FunctionalTests/FiltersTest.cs index 703a7c9888..8e13ae4a60 100644 --- a/test/Microsoft.AspNet.Mvc.FunctionalTests/FiltersTest.cs +++ b/test/Microsoft.AspNet.Mvc.FunctionalTests/FiltersTest.cs @@ -588,6 +588,71 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests Assert.Equal("Throwing Exception Filter", await response.Content.ReadAsStringAsync()); } + [Fact] + public async Task ResourceFilter_ShortCircuitsUsingObjectResult_UsesOptions() + { + // Arrange + var input = "{ sampleInt: 10 }"; + + var server = TestHelper.CreateServer(_app, SiteName, _configureServices); + var client = server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/ResourceFilter/Post"); + request.Content = new StringContent(input, Encoding.UTF8, "application/json"); + + // Act + var response = await client.SendAsync(request); + + // Assert + // Uses formatters from options. + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + // Notice this has been formatted using StringOutputFormatter and not JsonOutputFormatter. + Assert.Equal("someValue", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task ResourceFilter_ShortCircuitsUsingObjectResult_WithJsonFormatter_ReturnsResponse() + { + // Arrange + var input = "{ sampleInt: 10 }"; + + var server = TestHelper.CreateServer(_app, SiteName, _configureServices); + var client = server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/ResourceFilter/Get"); + request.Content = new StringContent(input, Encoding.UTF8, "application/json"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); + Assert.Equal("\"someValue\"", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task ResourceFilter_ChangesOutputFormatters_JsonReturned() + { + // Arrange + var input = "{ sampleInt: 10 }"; + + var server = TestHelper.CreateServer(_app, SiteName, _configureServices); + var client = server.CreateClient(); + + var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Json"); + request.Content = new StringContent(input, Encoding.UTF8, "application/json"); + + // Act + var response = await client.SendAsync(request); + + // Assert + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); + Assert.Equal("\"10\"", await response.Content.ReadAsStringAsync()); + } + [Fact] public async Task ResourceFilter_ChangesInputFormatters_JsonAccepted() { @@ -605,7 +670,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("10", await response.Content.ReadAsStringAsync()); + Assert.Equal("\"10\"", await response.Content.ReadAsStringAsync()); } [Fact] @@ -630,7 +695,7 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); - Assert.Equal("0", await response.Content.ReadAsStringAsync()); + Assert.Equal("\"0\"", await response.Content.ReadAsStringAsync()); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs index 671a80236a..6a554ba2ac 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/BadRequestErrorMessageResultTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; using Microsoft.AspNet.WebUtilities; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -72,6 +73,15 @@ namespace System.Web.Http var optionsAccessor = new Mock>(); optionsAccessor.SetupGet(o => o.Options) .Returns(options); + + var mockActionBindingContext = new Mock>(); + var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); services.Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs index f805f66d84..2075612099 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/ExceptionResultTest.cs @@ -9,6 +9,7 @@ using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; using Microsoft.AspNet.WebUtilities; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -73,6 +74,15 @@ namespace System.Web.Http optionsAccessor.SetupGet(o => o.Options) .Returns(options); + var mockActionBindingContext = new Mock>(); + var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + services.Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs index 4b5d274918..f8a5c0d713 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/InvalidModelStateResultTest.cs @@ -10,6 +10,7 @@ using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Routing; using Microsoft.AspNet.WebUtilities; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -86,6 +87,16 @@ namespace System.Web.Http optionsAccessor.SetupGet(o => o.Options) .Returns(options); + var mockActionBindingContext = new Mock>(); + + var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + services.Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs index f7491fe002..9df58b3768 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/NegotiatedContentResultTest.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -74,6 +75,15 @@ namespace System.Web.Http optionsAccessor.SetupGet(o => o.Options) .Returns(options); + var mockActionBindingContext = new Mock>(); + var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + services.Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); diff --git a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs index daa5d442e6..ba3d6c0207 100644 --- a/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.WebApiCompatShimTest/ActionResults/OkNegotiatedContentResultTest.cs @@ -8,6 +8,7 @@ using Microsoft.AspNet.Http.Core; using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; using Microsoft.AspNet.WebUtilities; +using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; using Moq; using Xunit; @@ -47,6 +48,15 @@ namespace System.Web.Http optionsAccessor.SetupGet(o => o.Options) .Returns(options); + var mockActionBindingContext = new Mock>(); + var bindingContext = new ActionBindingContext { OutputFormatters = options.OutputFormatters }; + mockActionBindingContext + .SetupGet(o => o.Value) + .Returns(bindingContext); + + services.Setup(o => o.GetService(typeof(IScopedInstance))) + .Returns(mockActionBindingContext.Object); + services.Setup(s => s.GetService(typeof(IOptions))) .Returns(optionsAccessor.Object); diff --git a/test/WebSites/FiltersWebSite/Controllers/JsonOnlyController.cs b/test/WebSites/FiltersWebSite/Controllers/JsonOnlyController.cs index 5898155df4..993e54873e 100644 --- a/test/WebSites/FiltersWebSite/Controllers/JsonOnlyController.cs +++ b/test/WebSites/FiltersWebSite/Controllers/JsonOnlyController.cs @@ -32,6 +32,11 @@ namespace FiltersWebSite.Controllers context.InputFormatters.Clear(); context.InputFormatters.Add(jsonFormatter); + + // Update the output formatter collection to only return JSON. + var jsonOutputFormatter = context.OutputFormatters.OfType().Single(); + context.OutputFormatters.Clear(); + context.OutputFormatters.Add(jsonOutputFormatter); } } } diff --git a/test/WebSites/FiltersWebSite/Controllers/ResourceFilterController.cs b/test/WebSites/FiltersWebSite/Controllers/ResourceFilterController.cs new file mode 100644 index 0000000000..49a34a30f3 --- /dev/null +++ b/test/WebSites/FiltersWebSite/Controllers/ResourceFilterController.cs @@ -0,0 +1,65 @@ +// 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.Linq; +using Microsoft.AspNet.Mvc; + +namespace FiltersWebSite.Controllers +{ + [Route("ResourceFilter/[action]")] + public class ResourceFilterController : Controller + { + [HttpPost] + [ShortCircuitWithFormatter] + public string Get() + { + return "NeverGetsExecuted"; + } + + [HttpPost] + [ShortCircuit] + public string Post() + { + return "NeverGetsExecuted"; + } + + private class ShortCircuitWithFormatterAttribute : Attribute, IResourceFilter + { + private IOutputFormatter[] _formatters; + + public ShortCircuitWithFormatterAttribute() + { + _formatters = new IOutputFormatter[] { new JsonOutputFormatter() }; + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + + public void OnResourceExecuting(ResourceExecutingContext context) + { + var result = new ObjectResult("someValue"); + foreach (var formatter in _formatters) + { + result.Formatters.Add(formatter); + } + + context.Result = result; + } + } + + private class ShortCircuitAttribute : Attribute, IResourceFilter + { + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + + public void OnResourceExecuting(ResourceExecutingContext context) + { + // ShortCircuit. + context.Result = new ObjectResult("someValue"); + } + } + } +} \ No newline at end of file