From 0b8fe87596997c426eb98f8e629f9ff19ae2d670 Mon Sep 17 00:00:00 2001 From: ryanbrandenburg Date: Mon, 12 Oct 2015 12:32:50 -0700 Subject: [PATCH] * Allow null ViewData and TempData --- .../ViewComponentResult.cs | 12 +++- .../ViewFeatures/ViewExecutor.cs | 8 ++- .../ViewComponentResultTest.cs | 45 +++++++++++--- .../DefaultDisplayTemplatesTest.cs | 11 ++-- .../ViewFeatures/ViewExecutorTest.cs | 58 +++++++++++++++++++ 5 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs index 6343cbccd6..372078f107 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewComponentResult.cs @@ -84,6 +84,12 @@ namespace Microsoft.AspNet.Mvc viewData = new ViewDataDictionary(modelMetadataProvider, context.ModelState); } + var tempData = TempData; + if (tempData == null) + { + tempData = services.GetRequiredService(); + } + var contentType = ContentType; if (contentType != null && contentType.Encoding == null) { @@ -98,8 +104,8 @@ namespace Microsoft.AspNet.Mvc // 3. ViewExecutor.DefaultContentType (sensible default) // // - response.ContentType = - contentType?.ToString() ?? + response.ContentType = + contentType?.ToString() ?? response.ContentType ?? ViewExecutor.DefaultContentType.ToString(); @@ -115,7 +121,7 @@ namespace Microsoft.AspNet.Mvc context, NullView.Instance, viewData, - TempData, + tempData, writer, htmlHelperOptions); diff --git a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs index dee4e8ec37..0aa5f9b1c9 100644 --- a/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs +++ b/src/Microsoft.AspNet.Mvc.ViewFeatures/ViewFeatures/ViewExecutor.cs @@ -6,8 +6,10 @@ using System.Diagnostics; using System.Text; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.Infrastructure; +using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewEngines; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.OptionsModel; using Microsoft.Net.Http.Headers; @@ -117,14 +119,16 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures throw new ArgumentNullException(nameof(view)); } + var services = actionContext.HttpContext.RequestServices; if (viewData == null) { - throw new ArgumentNullException(nameof(viewData)); + var metadataProvider = services.GetRequiredService(); + viewData = new ViewDataDictionary(metadataProvider); } if (tempData == null) { - throw new ArgumentNullException(nameof(tempData)); + tempData = services.GetRequiredService(); } var response = actionContext.HttpContext.Response; diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs index f9185f9eba..f9b6bfe39d 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewComponentResultTest.cs @@ -29,6 +29,32 @@ namespace Microsoft.AspNet.Mvc private readonly ITempDataDictionary _tempDataDictionary = new TempDataDictionary(new HttpContextAccessor(), new SessionStateTempDataProvider()); + [Fact] + public async Task ExecuteAsync_ViewComponentResult_AllowsNullViewDataAndTempData() + { + // Arrange + var descriptor = new ViewComponentDescriptor() + { + FullName = "Full.Name.Text", + ShortName = "Text", + Type = typeof(TextViewComponent), + }; + + var actionContext = CreateActionContext(descriptor); + + var viewComponentResult = new ViewComponentResult + { + Arguments = new object[] { "World!" }, + ViewData = null, + TempData = null, + ViewComponentName = "Text" + }; + + // Act + await viewComponentResult.ExecuteResultAsync(actionContext); + // No assert, just confirm it didn't throw + } + [Fact] public async Task ExecuteResultAsync_Throws_IfNameOrTypeIsNotSet() { @@ -76,10 +102,10 @@ namespace Microsoft.AspNet.Mvc // Arrange var expected = $"A view component named '{typeof(TextViewComponent).FullName}' could not be found."; - var services = CreateServices(); + var actionContext = CreateActionContext(); + var services = CreateServices(actionContext.HttpContext); services.AddSingleton(); - var actionContext = CreateActionContext(); var viewComponentResult = new ViewComponentResult { @@ -387,8 +413,11 @@ namespace Microsoft.AspNet.Mvc Assert.Equal(expectedContentType, actionContext.HttpContext.Response.ContentType); } - private IServiceCollection CreateServices(params ViewComponentDescriptor[] descriptors) - { + private IServiceCollection CreateServices(HttpContext context, params ViewComponentDescriptor[] descriptors) + { + var httpContext = new HttpContextAccessor() { HttpContext = context }; + var tempDataProvider = new SessionStateTempDataProvider(); + var services = new ServiceCollection(); services.AddSingleton, TestOptionsManager>(); services.AddTransient(); @@ -400,15 +429,17 @@ namespace Microsoft.AspNet.Mvc services.AddInstance(new FixedSetViewComponentDescriptorProvider(descriptors)); services.AddSingleton(); services.AddInstance(NullLoggerFactory.Instance); + services.AddInstance(new TempDataDictionary(httpContext, tempDataProvider)); + services.AddTransient(); return services; } private HttpContext CreateHttpContext(params ViewComponentDescriptor[] descriptors) { - var services = CreateServices(descriptors); - var httpContext = new DefaultHttpContext(); + var services = CreateServices(httpContext, descriptors); + httpContext.Response.Body = new MemoryStream(); httpContext.RequestServices = services.BuildServiceProvider(); @@ -467,4 +498,4 @@ namespace Microsoft.AspNet.Mvc } } } -} \ No newline at end of file +} diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/DefaultDisplayTemplatesTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/DefaultDisplayTemplatesTest.cs index 56152026af..de4d68c093 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/DefaultDisplayTemplatesTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/DefaultDisplayTemplatesTest.cs @@ -122,12 +122,11 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures public void ObjectTemplate_IgnoresPropertiesWith_ScaffoldColumnFalse() { // Arrange - var expected = -@"
HtmlEncode[[Property1]]
-
-
HtmlEncode[[Property3]]
-
-"; + var expected = "
HtmlEncode[[Property1]]
" + Environment.NewLine + + "
"+ Environment.NewLine + + "
HtmlEncode[[Property3]]
"+ Environment.NewLine + + "
"+ Environment.NewLine; + var model = new DefaultTemplatesUtilities.ObjectWithScaffoldColumn(); var viewEngine = new Mock(); viewEngine.Setup(v => v.FindPartialView(It.IsAny(), It.IsAny())) diff --git a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs index 9290b4e090..7c9df6ac6c 100644 --- a/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs +++ b/test/Microsoft.AspNet.Mvc.ViewFeatures.Test/ViewFeatures/ViewExecutorTest.cs @@ -8,12 +8,14 @@ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Internal; using Microsoft.AspNet.Mvc.Abstractions; using Microsoft.AspNet.Mvc.ModelBinding; using Microsoft.AspNet.Mvc.Rendering; using Microsoft.AspNet.Mvc.ViewEngines; using Microsoft.AspNet.Routing; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Net.Http.Headers; using Moq; using Xunit; @@ -112,6 +114,62 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures Assert.Equal("abcd", Encoding.UTF8.GetString(memoryStream.ToArray())); } + private static IServiceProvider GetServiceProvider() + { + var httpContext = new HttpContextAccessor() { HttpContext = new DefaultHttpContext() }; + var serviceCollection = new ServiceCollection(); + serviceCollection.AddInstance(new EmptyModelMetadataProvider()); + var tempDataProvider = new SessionStateTempDataProvider(); + serviceCollection.AddInstance(new TempDataDictionary(httpContext, tempDataProvider)); + + return serviceCollection.BuildServiceProvider(); + } + + [Fact] + public async Task ExecuteAsync_ViewResultAllowNull() + { + // Arrange + var tempDataNull = false; + var viewDataNull = false; + var deligateHit = false; + + var view = CreateView(async (v) => + { + deligateHit = true; + tempDataNull = v.TempData == null; + viewDataNull = v.ViewData == null; + + await v.Writer.WriteAsync("abcd"); + }); + var context = new DefaultHttpContext(); + + var memoryStream = new MemoryStream(); + context.Response.Body = memoryStream; + + var actionContext = new ActionContext( + context, + new RouteData(), + new ActionDescriptor()); + + context.RequestServices = GetServiceProvider(); + var viewExecutor = CreateViewExecutor(); + + // Act + await viewExecutor.ExecuteAsync( + actionContext, + view, + null, + null, + contentType: null, + statusCode: 200); + + // Assert + Assert.Equal(200, context.Response.StatusCode); + Assert.True(deligateHit); + Assert.False(viewDataNull); + Assert.False(tempDataNull); + } + [Fact] public async Task ExecuteAsync_SetsStatusCode() {