From d7f2630ad2e1ba1aa1ae83cfd2062e48bd93ad54 Mon Sep 17 00:00:00 2001 From: Mugdha Kulkarni Date: Tue, 27 Jan 2015 13:56:06 -0800 Subject: [PATCH] Making FormatFilterAttribute an IFilterFactory --- .../Controllers/FormatFilterController.cs | 5 +- samples/MvcSample.Web/Startup.cs | 1 - .../Filters/FormatFilter.cs | 136 +++++++ .../Filters/FormatFilterAttribute.cs | 120 +----- .../Filters/IFormatFilter.cs | 20 +- .../Filters/ProducesAttribute.cs | 2 +- .../FormatterMappings.cs | 4 +- src/Microsoft.AspNet.Mvc/MvcServices.cs | 2 + .../Filters/FormatFilterTest.cs | 363 ++++++++++-------- .../Filters/ProducesAttributeTests.cs | 6 +- 10 files changed, 373 insertions(+), 286 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilter.cs diff --git a/samples/MvcSample.Web/Controllers/FormatFilterController.cs b/samples/MvcSample.Web/Controllers/FormatFilterController.cs index 945331cd76..5debd2d10b 100644 --- a/samples/MvcSample.Web/Controllers/FormatFilterController.cs +++ b/samples/MvcSample.Web/Controllers/FormatFilterController.cs @@ -5,14 +5,15 @@ using Microsoft.AspNet.Mvc; namespace MvcSample.Web.Controllers { + [Route("[controller]/[action]/{id}.{format?}")] public class FormatFilterController : Controller { public Product GetProduct(int id) { return new Product() { SampleInt = id }; } - - [Produces("application/custom", "application/json", "text/json")] + + [Produces("application/json", "text/json")] public Product ProducesMethod(int id) { return new Product() { SampleInt = id }; ; diff --git a/samples/MvcSample.Web/Startup.cs b/samples/MvcSample.Web/Startup.cs index 38763af5cd..023640a31c 100644 --- a/samples/MvcSample.Web/Startup.cs +++ b/samples/MvcSample.Web/Startup.cs @@ -114,7 +114,6 @@ namespace MvcSample.Web app.UseMvc(routes => { - routes.MapRoute("FormatRoute", "{controller}/{action}/{id}.{format?}"); routes.MapRoute("areaRoute", "{area:exists}/{controller}/{action}"); routes.MapRoute( "controllerActionRoute", diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilter.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilter.cs new file mode 100644 index 0000000000..b2043a4dde --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilter.cs @@ -0,0 +1,136 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Mvc.Description; +using Microsoft.Net.Http.Headers; + +namespace Microsoft.AspNet.Mvc +{ + /// + /// A filter which will use the format value in the route data or query string to set the content type on an + /// returned from an action. + /// + public class FormatFilter : IFormatFilter, IResourceFilter, IResultFilter + { + /// + /// Initializes an instance of . + /// + /// . + public FormatFilter(MvcOptions options, ActionContext actionContext) + { + IsActive = true; + Format = GetFormat(actionContext); + + if (string.IsNullOrEmpty(Format)) + { + IsActive = false; + return; + } + + ContentType = options.FormatterMappings.GetMediaTypeMappingForFormat(Format); + } + + /// + /// format value in the current request. null if format not present in the current request. + /// + public string Format { get; private set; } + + /// + /// for the format value in the current request. + /// + public MediaTypeHeaderValue ContentType { get; private set; } + + /// + /// true if the current is active and will execute. + /// + public bool IsActive { get; private set; } + + /// + /// As a , this filter looks at the request and rejects it before going ahead if + /// 1. The format in the request doesnt match any format in the map. + /// 2. If there is a conflicting producesFilter. + /// + /// The . + public void OnResourceExecuting([NotNull] ResourceExecutingContext context) + { + if (!IsActive) + { + return; // no format specified by user, so the filter is muted + } + + if (ContentType == null) + { + // no contentType exists for the format, return 404 + context.Result = new HttpNotFoundResult(); + } + else + { + var responseTypeFilters = context.Filters.OfType(); + var contentTypes = new List(); + + foreach (var filter in responseTypeFilters) + { + filter.SetContentTypes(contentTypes); + } + + if (contentTypes.Count != 0) + { + // There is no IApiResponseMetadataProvider to generate the content type user asked for. We have to + // exit here with not found result. + if (!contentTypes.Any(c => ContentType.IsSubsetOf(c))) + { + context.Result = new HttpNotFoundResult(); + } + } + } + } + + /// + public void OnResourceExecuted([NotNull] ResourceExecutedContext context) + { + } + + /// + /// Sets a Content Type on an using a format value from the request. + /// + /// The . + public void OnResultExecuting([NotNull] ResultExecutingContext context) + { + if (!IsActive) + { + return; // no format specified by user, so the filter is muted + } + + var objectResult = context.Result as ObjectResult; + if (objectResult != null) + { + objectResult.ContentTypes.Clear(); + objectResult.ContentTypes.Add(ContentType); + } + } + + /// + public void OnResultExecuted([NotNull] ResultExecutedContext context) + { + } + + private string GetFormat(ActionContext context) + { + object format = null; + + if (!context.RouteData.Values.TryGetValue("format", out format)) + { + format = context.HttpContext.Request.Query["format"]; + } + + if (format != null) + { + return format.ToString(); + } + + return null; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilterAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilterAttribute.cs index 1950971929..8836373227 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilterAttribute.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/FormatFilterAttribute.cs @@ -2,13 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Microsoft.AspNet.Mvc.Description; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; -using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Mvc { @@ -17,117 +12,18 @@ namespace Microsoft.AspNet.Mvc /// returned from an action. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] - public class FormatFilterAttribute : Attribute, IFormatFilter, IResourceFilter, IResultFilter + public class FormatFilterAttribute : Attribute, IFilterFactory { /// - /// As a , this filter looks at the request and rejects it before going ahead if - /// 1. The format in the request doesnt match any format in the map. - /// 2. If there is a conflicting producesFilter. + /// Creates an instance of /// - /// The . - public void OnResourceExecuting([NotNull] ResourceExecutingContext context) + /// The + /// An instance of + public IFilter CreateInstance([NotNull] IServiceProvider serviceProvider) { - var format = GetFormat(context); - - if (!string.IsNullOrEmpty(format)) - { - var formatContentType = GetContentType(format, context); - if (formatContentType == null) - { - // no contentType exists for the format, return 404 - context.Result = new HttpNotFoundResult(); - } - else - { - var responseTypeFilters = context.Filters.OfType(); - var contentTypes = new List(); - - foreach (var filter in responseTypeFilters) - { - filter.SetContentTypes(contentTypes); - } - - if (contentTypes.Count != 0) - { - // If formatfilterContentType is not subset of any of the content types produced by - // IApiResponseMetadataProviders, return 404 - if (!contentTypes.Any(c => formatContentType.IsSubsetOf(c))) - { - context.Result = new HttpNotFoundResult(); - } - } - } - } - } - - /// - public void OnResourceExecuted([NotNull] ResourceExecutedContext context) - { - - } - - /// - /// Sets a Content Type on an using a format value from the request. - /// - /// The . - public void OnResultExecuting([NotNull] ResultExecutingContext context) - { - var format = GetFormat(context); - if (!string.IsNullOrEmpty(format)) - { - var objectResult = context.Result as ObjectResult; - if (objectResult != null) - { - var contentType = GetContentType(format, context); - Debug.Assert(contentType != null); - objectResult.ContentTypes.Clear(); - objectResult.ContentTypes.Add(contentType); - } - } - } - - /// - public void OnResultExecuted([NotNull] ResultExecutedContext context) - { - - } - - /// - /// Returns true if the filter is active and will execute; otherwise, false. The filter is active - /// if the current request contains format value. - /// - /// The - /// true if the current request contains format value; otherwise, false. - public bool IsActive(FilterContext context) - { - var format = GetFormat(context); - - return !string.IsNullOrEmpty(format); - } - - private string GetFormat(FilterContext context) - { - object format = null; - - if (!context.RouteData.Values.TryGetValue("format", out format)) - { - format = context.HttpContext.Request.Query["format"]; - } - - if (format != null) - { - return format.ToString(); - } - - return null; - } - - private MediaTypeHeaderValue GetContentType(string format, FilterContext context) - { - var options = context.HttpContext.RequestServices.GetRequiredService>(); - var contentType = options.Options.FormatterMappings.GetMediaTypeMappingForFormat(format); - - return contentType; + var options = serviceProvider.GetRequiredService>(); + var actionContext = serviceProvider.GetRequiredService>(); + return new FormatFilter(options.Options, actionContext.Value); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/IFormatFilter.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/IFormatFilter.cs index 09ebc7e262..0b05f4c535 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/IFormatFilter.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/IFormatFilter.cs @@ -1,6 +1,8 @@ // 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 Microsoft.Net.Http.Headers; + namespace Microsoft.AspNet.Mvc { /// @@ -9,12 +11,18 @@ namespace Microsoft.AspNet.Mvc public interface IFormatFilter : IFilter { /// - /// Returns true if the filter will produce a content type for the current request, otherwise - /// false. + /// format value in the current request. null if format not present in the current request. /// - /// The - /// true if the filter will produce a content type for the current request; otherwise, - /// false. - bool IsActive(FilterContext context); + string Format { get; } + + /// + /// for the format value in the current request. + /// + MediaTypeHeaderValue ContentType { get; } + + /// + /// true if the current is active and will execute. + /// + bool IsActive { get; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs b/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs index a3e74c662d..2a12120ec5 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Filters/ProducesAttribute.cs @@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Mvc { // Check if there are any IFormatFilter in the pipeline, and if any of them is active. If there is one, // do not override the content type value. - if (context.Filters.OfType().All(f => !f.IsActive(context))) + if (context.Filters.OfType().All(f => !f.IsActive)) { SetContentTypes(objectResult.ContentTypes); } diff --git a/src/Microsoft.AspNet.Mvc.Core/FormatterMappings.cs b/src/Microsoft.AspNet.Mvc.Core/FormatterMappings.cs index b759c9dadb..b1f18673e1 100644 --- a/src/Microsoft.AspNet.Mvc.Core/FormatterMappings.cs +++ b/src/Microsoft.AspNet.Mvc.Core/FormatterMappings.cs @@ -3,10 +3,8 @@ using System; using System.Collections.Generic; -using Microsoft.Net.Http.Headers; -using System.Collections.ObjectModel; -using System.Globalization; using Microsoft.AspNet.Mvc.Core; +using Microsoft.Net.Http.Headers; namespace Microsoft.AspNet.Mvc { diff --git a/src/Microsoft.AspNet.Mvc/MvcServices.cs b/src/Microsoft.AspNet.Mvc/MvcServices.cs index 745481bd5b..8ce05b618e 100644 --- a/src/Microsoft.AspNet.Mvc/MvcServices.cs +++ b/src/Microsoft.AspNet.Mvc/MvcServices.cs @@ -75,6 +75,8 @@ namespace Microsoft.AspNet.Mvc yield return describe.Transient, DefaultFilterProvider>(); + yield return describe.Transient(); + // Dataflow - ModelBinding, Validation and Formatting yield return describe.Transient(); diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/FormatFilterTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/FormatFilterTest.cs index 25e581643b..001c0e8cc7 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/FormatFilterTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/FormatFilterTest.cs @@ -3,8 +3,8 @@ using System; using Microsoft.AspNet.Http; -using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Http.Core; +using Microsoft.AspNet.Mvc; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; @@ -37,16 +37,15 @@ namespace Microsoft.AspNet.Mvc FormatSource place, string contentType) { - // Arrange + // Arrange var mediaType = MediaTypeHeaderValue.Parse("application/json"); - var resultExecutingContext = CreateResultExecutingContext( - format, - FormatSource.RouteData); - var resourceExecutingContext = CreateResourceExecutingContext( - new IFilter[] { }, - format, - FormatSource.RouteData); - var filter = new FormatFilterAttribute(); + var mockObjects = new MockObjects(format, place); + + var resultExecutingContext = mockObjects.CreateResultExecutingContext(); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { }); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -70,16 +69,21 @@ namespace Microsoft.AspNet.Mvc // Arrange var mediaType = MediaTypeHeaderValue.Parse("application/json"); + var mockObjects = new MockObjects("json", FormatSource.RouteData); - var httpContext = CreateMockHttpContext(); + var httpContext = new Mock(); + httpContext + .Setup(c => c.RequestServices) + .Returns(mockObjects.MockServiceProvider); + + // Query contains xml + httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true); + httpContext.Setup(c => c.Request.Query.Get("format")).Returns("xml"); // Routedata contains json var data = new RouteData(); data.Values.Add("format", "json"); - // Query contains xml - httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true); - httpContext.Setup(c => c.Request.Query.Get("format")).Returns("xml"); var ac = new ActionContext(httpContext.Object, data, new ActionDescriptor()); var resultExecutingContext = new ResultExecutingContext( @@ -92,7 +96,8 @@ namespace Microsoft.AspNet.Mvc ac, new IFilter[] { }); - var filter = new FormatFilterAttribute(); + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -115,14 +120,18 @@ namespace Microsoft.AspNet.Mvc { // Arrange var mediaType = MediaTypeHeaderValue.Parse(contentType); - var resultExecutingContext = CreateResultExecutingContext(format, place); - var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place); - var options = resultExecutingContext.HttpContext.RequestServices.GetService>(); + + var mockObjects = new MockObjects(format, place); + var resultExecutingContext = mockObjects.CreateResultExecutingContext(); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { }); + + var options = mockObjects.MockServiceProvider.GetService>(); options.Options.FormatterMappings.SetMediaTypeMappingForFormat( format, MediaTypeHeaderValue.Parse(contentType)); - - var filter = new FormatFilterAttribute(); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -135,17 +144,18 @@ namespace Microsoft.AspNet.Mvc } [Theory] - [InlineData("foo", FormatSource.RouteData, "application/foo")] - [InlineData("foo", FormatSource.QueryData, "application/foo")] + [InlineData("foo", FormatSource.RouteData)] + [InlineData("foo", FormatSource.QueryData)] public void FormatFilter_ContextContainsNonExistingFormat( string format, - FormatSource place, - string contentType) + FormatSource place) { - // Arrange - var mediaType = MediaTypeHeaderValue.Parse(contentType); - var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }, format, place); - var filter = new FormatFilterAttribute(); + // Arrange + var mockObjects = new MockObjects(format, place); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { }); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -159,8 +169,11 @@ namespace Microsoft.AspNet.Mvc public void FormatFilter_ContextDoesntContainFormat() { // Arrange - var resourceExecutingContext = CreateResourceExecutingContext(new IFilter[] { }); - var filter = new FormatFilterAttribute(); + var mockObjects = new MockObjects(); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { }); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -179,14 +192,17 @@ namespace Microsoft.AspNet.Mvc { // Arrange var produces = new ProducesAttribute(contentType, new string[] { "application/foo", "text/bar" }); - var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place); - var filter = new FormatFilterAttribute(); + var mockObjects = new MockObjects(format, place); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { produces }); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act - filter.OnResourceExecuting(context); + filter.OnResourceExecuting(resourceExecutingContext); // Assert - Assert.Null(context.Result); + Assert.Null(resourceExecutingContext.Result); } [Fact] @@ -194,18 +210,21 @@ namespace Microsoft.AspNet.Mvc { // Arrange var produces = new ProducesAttribute("application/xml;version=1", new string [] { }); - var context = CreateResourceExecutingContext(new IFilter[] { produces }, "xml", FormatSource.RouteData); - var options = context.HttpContext.RequestServices.GetService>(); + var mockObjects = new MockObjects("xml", FormatSource.RouteData); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { produces }); + var options = mockObjects.MockServiceProvider.GetService>(); options.Options.FormatterMappings.SetMediaTypeMappingForFormat( "xml", MediaTypeHeaderValue.Parse("application/xml")); - var filter = new FormatFilterAttribute(); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act - filter.OnResourceExecuting(context); + filter.OnResourceExecuting(resourceExecutingContext); // Assert - Assert.Null(context.Result); + Assert.Null(resourceExecutingContext.Result); } [Fact] @@ -213,18 +232,22 @@ namespace Microsoft.AspNet.Mvc { // Arrange var produces = new ProducesAttribute("application/*", new string[] { }); - var context = CreateResourceExecutingContext(new IFilter[] { produces }, "xml", FormatSource.RouteData); - var options = context.HttpContext.RequestServices.GetService>(); + + var mockObjects = new MockObjects("xml", FormatSource.RouteData); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { produces }); + var options = mockObjects.MockServiceProvider.GetService>(); options.Options.FormatterMappings.SetMediaTypeMappingForFormat( "xml", MediaTypeHeaderValue.Parse("application/xml")); - var filter = new FormatFilterAttribute(); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act - filter.OnResourceExecuting(context); + filter.OnResourceExecuting(resourceExecutingContext); // Assert - Assert.Null(context.Result); + Assert.Null(resourceExecutingContext.Result); } [Fact] @@ -232,42 +255,50 @@ namespace Microsoft.AspNet.Mvc { // Arrange var produces = new ProducesAttribute("application/xml", new string[] { }); - var context = CreateResourceExecutingContext(new IFilter[] { produces }, "xml", FormatSource.RouteData); - var options = context.HttpContext.RequestServices.GetService>(); + var mockObjects = new MockObjects("xml", FormatSource.RouteData); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { produces }); + var options = mockObjects.MockServiceProvider.GetService>(); options.Options.FormatterMappings.SetMediaTypeMappingForFormat( - "xml", + "xml", MediaTypeHeaderValue.Parse("application/xml;version=1")); - var filter = new FormatFilterAttribute(); + + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act - filter.OnResourceExecuting(context); + filter.OnResourceExecuting(resourceExecutingContext); // Assert - var actionResult = context.Result; + var actionResult = resourceExecutingContext.Result; Assert.IsType(actionResult); } [Theory] - [InlineData("json", FormatSource.RouteData, "application/json")] - [InlineData("json", FormatSource.QueryData, "application/json")] + [InlineData("json", FormatSource.RouteData)] + [InlineData("json", FormatSource.QueryData)] public void FormatFilter_ContextContainsFormat_ContainsProducesFilter_Conflicting( string format, - FormatSource place, - string contentType) + FormatSource place) { // Arrange - var mediaType = MediaTypeHeaderValue.Parse(contentType); var produces = new ProducesAttribute("application/xml", new string[] { "application/foo", "text/bar" }); - var context = CreateResourceExecutingContext(new IFilter[] { produces }, format, place); - var filter = new FormatFilterAttribute(); + var mockObjects = new MockObjects(format, place); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { produces }); + var options = mockObjects.MockServiceProvider.GetService>(); + options.Options.FormatterMappings.SetMediaTypeMappingForFormat( + "xml", + MediaTypeHeaderValue.Parse("application/xml")); + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); + // Act - filter.OnResourceExecuting(context); + filter.OnResourceExecuting(resourceExecutingContext); // Assert - var result = Assert.IsType(context.Result); + var result = Assert.IsType(resourceExecutingContext.Result); } - + [Theory] [InlineData("", FormatSource.RouteData)] [InlineData(null, FormatSource.QueryData)] @@ -277,12 +308,11 @@ namespace Microsoft.AspNet.Mvc string format, FormatSource place) { - // Arrange - var resourceExecutingContext = CreateResourceExecutingContext( - new IFilter[] { }, - format, - place); - var filter = new FormatFilterAttribute(); + // Arrange + var mockObjects = new MockObjects(format, place); + var resourceExecutingContext = mockObjects.CreateResourceExecutingContext(new IFilter[] { }); + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); // Act filter.OnResourceExecuting(resourceExecutingContext); @@ -301,99 +331,14 @@ namespace Microsoft.AspNet.Mvc FormatSource place, bool expected) { - // Arrange - var resultExecutingContext = CreateResultExecutingContext(format, place); - var filter = new FormatFilterAttribute(); + // Arrange + var mockObjects = new MockObjects(format, place); + var resultExecutingContext = mockObjects.CreateResultExecutingContext(); + var filterAttribute = new FormatFilterAttribute(); + var filter = (FormatFilter)filterAttribute.CreateInstance(mockObjects.MockServiceProvider); - // Act - var isActive = filter.IsActive(resultExecutingContext); - - // Assert - Assert.Equal(expected, isActive); - } - - private static ResourceExecutingContext CreateResourceExecutingContext( - IFilter[] filters, - string format = null, - FormatSource? place = null) - { - if (format == null || place == null) - { - var context = new ResourceExecutingContext( - CreateActionContext(), - filters); - return context; - } - - var context1 = new ResourceExecutingContext( - CreateActionContext(format, place), - filters); - return context1; - } - - private static ResultExecutingContext CreateResultExecutingContext( - string format = null, - FormatSource? place = null) - { - if (format == null && place == null) - { - return new ResultExecutingContext( - new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor()), - new IFilter[] { }, - new ObjectResult("Some Value"), - controller: new object()); - } - - return new ResultExecutingContext( - CreateActionContext(format, place), - new IFilter[] { }, - new ObjectResult("Some Value"), - controller: new object()); - } - - private static ActionContext CreateActionContext(string format = null, FormatSource? place = null) - { - var httpContext = CreateMockHttpContext(); - var data = new RouteData(); - - if (place == FormatSource.RouteData || place == FormatSource.RouteAndQueryData) - { - data.Values.Add("format", format); - httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); - } - - if (place == FormatSource.QueryData || place == FormatSource.RouteAndQueryData) - { - httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true); - httpContext.Setup(c => c.Request.Query["format"]).Returns(format); - } - else if (place == null && format == null) - { - httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); - } - - return new ActionContext(httpContext.Object, data, new ActionDescriptor()); - } - - private static Mock CreateMockHttpContext() - { - var options = new MvcOptions(); - MvcOptionsSetup.ConfigureMvc(options); - var mvcOptions = new Mock>(); - mvcOptions.Setup(o => o.Options).Returns(options); - - var serviceProvider = new Mock(); - serviceProvider - .Setup(s => s.GetService(It.Is(t => t == typeof(IOptions)))) - .Returns(mvcOptions.Object); - - var httpContext = new Mock(); - httpContext - .Setup(c => c.RequestServices) - .Returns(serviceProvider.Object); - - httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); - return httpContext; + // Act and Assert + Assert.Equal(expected, filter.IsActive); } private static void AssertMediaTypesEqual( @@ -411,6 +356,108 @@ namespace Microsoft.AspNet.Mvc Assert.Equal(item.Value, NameValueHeaderValue.Find(actualMediaType.Parameters, item.Name).Value); } } + + public class MockObjects + { + public IServiceProvider MockServiceProvider { get; private set; } + public HttpContext MockHttpContext { get; private set; } + public ActionContext MockActionContext { get; private set; } + + public MockObjects(string format = null, FormatSource? place = null) + { + var httpContext = new Mock(); + MockServiceProvider = CreateMockServiceProvider(httpContext, format, place); + + httpContext + .Setup(c => c.RequestServices) + .Returns(MockServiceProvider); + + httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); + + MockHttpContext = httpContext.Object; + //MockActionContext = CreateMockActionContext(httpContext, format, place); + } + + public ResourceExecutingContext CreateResourceExecutingContext(IFilter[] filters) + { + var context = new ResourceExecutingContext( + MockActionContext, + filters); + return context; + } + + public ResultExecutingContext CreateResultExecutingContext() + { + return new ResultExecutingContext( + MockActionContext, + new IFilter[] { }, + new ObjectResult("Some Value"), + controller: new object()); + } + + private ActionContext CreateMockActionContext( + Mock httpContext, + string format, + FormatSource? place) + { + var data = new RouteData(); + + if (place == FormatSource.RouteData || place == FormatSource.RouteAndQueryData) + { + data.Values.Add("format", format); + httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); + } + + if (place == FormatSource.QueryData || place == FormatSource.RouteAndQueryData) + { + httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(true); + httpContext.Setup(c => c.Request.Query["format"]).Returns(format); + } + else if (place == null && format == null) + { + httpContext.Setup(c => c.Request.Query.ContainsKey("format")).Returns(false); + } + + return new ActionContext(httpContext.Object, data, new ActionDescriptor()); + } + + private IServiceProvider CreateMockServiceProvider( + Mock httpContext, + string format = null, + FormatSource? place = null) + { + // Setup options on mock service provider + var options = new MvcOptions(); + //MvcOptionsSetup.ConfigureMvc(options); + + // Set up default output formatters. + options.OutputFormatters.Add(new HttpNoContentOutputFormatter()); + options.OutputFormatters.Add(new StringOutputFormatter()); + options.OutputFormatters.Add(new JsonOutputFormatter()); + + // Set up default mapping for json extensions to content type + options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json")); + + var mvcOptions = new Mock>(); + mvcOptions.Setup(o => o.Options).Returns(options); + + var serviceProvider = new Mock(); + serviceProvider + .Setup(s => s.GetService(It.Is(t => t == typeof(IOptions)))) + .Returns(mvcOptions.Object); + + // Setup MVC services on mock service provider + MockActionContext = CreateMockActionContext(httpContext, format, place); + var scopedInstance = new Mock>(); + scopedInstance.Setup(s => s.Value).Returns(MockActionContext); + + serviceProvider + .Setup(s => s.GetService(It.Is(t => t == typeof(IScopedInstance)))) + .Returns(scopedInstance.Object); + + return serviceProvider.Object; + } + } #endif } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs index 6b9147d16c..d056dbd575 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/Filters/ProducesAttributeTests.cs @@ -37,7 +37,7 @@ namespace Microsoft.AspNet.Mvc.Test ValidateMediaType(mediaType1, objectResult.ContentTypes[0]); ValidateMediaType(mediaType2, objectResult.ContentTypes[1]); } - + [Fact] public async Task ProducesContentAttribute_FormatFilterAttribute_NotActive() { @@ -45,7 +45,7 @@ namespace Microsoft.AspNet.Mvc.Test var producesContentAttribute = new ProducesAttribute("application/xml"); var formatFilter = new Mock(); - formatFilter.Setup(f => f.IsActive(It.IsAny())) + formatFilter.Setup(f => f.IsActive) .Returns(false); var filters = new IFilter[] { producesContentAttribute, formatFilter.Object }; @@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Mvc.Test var producesContentAttribute = new ProducesAttribute("application/xml"); var formatFilter = new Mock(); - formatFilter.Setup(f => f.IsActive(It.IsAny())) + formatFilter.Setup(f => f.IsActive) .Returns(true); var filters = new IFilter[] { producesContentAttribute, formatFilter.Object };