Introduce ViewDataAttribute
Allow properties on controllers, Razor Page and Razor Page models annotatted with [ViewDataAttribute] to populate ViewDataDictionary Fixes https://github.com/aspnet/Mvc/issues/6525
This commit is contained in:
parent
c515cece8e
commit
07a1907918
|
|
@ -3,6 +3,7 @@
|
|||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Package Versions">
|
||||
<AngleSharpPackageVersion>0.9.9</AngleSharpPackageVersion>
|
||||
<BenchmarkDotNetPackageVersion>0.10.13</BenchmarkDotNetPackageVersion>
|
||||
<InternalAspNetCoreSdkPackageVersion>2.1.0-preview2-15749</InternalAspNetCoreSdkPackageVersion>
|
||||
<MicrosoftAspNetCoreAntiforgeryPackageVersion>2.1.0-preview2-30478</MicrosoftAspNetCoreAntiforgeryPackageVersion>
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, AuthorizationPageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, TempDataFilterPageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, ViewDataAttributePageApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Singleton<IPageApplicationModelProvider, ResponseCacheFilterApplicationModelProvider>());
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
|
|
@ -66,6 +67,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
pageContext.ViewData.Model = result.Model;
|
||||
}
|
||||
|
||||
OnExecuting(pageContext);
|
||||
|
||||
var viewStarts = new IRazorPage[pageContext.ViewStartFactories.Count];
|
||||
for (var i = 0; i < pageContext.ViewStartFactories.Count; i++)
|
||||
{
|
||||
|
|
@ -83,5 +86,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
return ExecuteAsync(viewContext, result.ContentType, result.StatusCode);
|
||||
}
|
||||
|
||||
private void OnExecuting(PageContext pageContext)
|
||||
{
|
||||
var viewDataValuesProvider = pageContext.HttpContext.Features.Get<IViewDataValuesProviderFeature>();
|
||||
if (viewDataValuesProvider != null)
|
||||
{
|
||||
viewDataValuesProvider.ProvideViewDataValues(pageContext.ViewData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright (c) .NET Foundation. 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 Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
internal class PageViewDataAttributeFilter : IPageFilter, IViewDataValuesProviderFeature
|
||||
{
|
||||
public PageViewDataAttributeFilter(IReadOnlyList<LifecycleProperty> properties)
|
||||
{
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
public IReadOnlyList<LifecycleProperty> Properties { get; }
|
||||
|
||||
public object Subject { get; set; }
|
||||
|
||||
public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
Subject = context.HandlerInstance;
|
||||
context.HttpContext.Features.Set<IViewDataValuesProviderFeature>(this);
|
||||
}
|
||||
|
||||
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void ProvideViewDataValues(ViewDataDictionary viewData)
|
||||
{
|
||||
for (var i = 0; i < Properties.Count; i++)
|
||||
{
|
||||
var property = Properties[i];
|
||||
var value = property.GetValue(Subject);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
viewData[property.Key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
internal class PageViewDataAttributeFilterFactory : IFilterFactory
|
||||
{
|
||||
public PageViewDataAttributeFilterFactory(IReadOnlyList<LifecycleProperty> properties)
|
||||
{
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
public IReadOnlyList<LifecycleProperty> Properties { get; }
|
||||
|
||||
// PageViewDataAttributeFilter is stateful and cannot be reused.
|
||||
public bool IsReusable => false;
|
||||
|
||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new PageViewDataAttributeFilter(Properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
internal class ViewDataAttributePageApplicationModelProvider : IPageApplicationModelProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <remarks>This order ensures that <see cref="ViewDataAttributePageApplicationModelProvider"/> runs after the <see cref="DefaultPageApplicationModelProvider"/>.</remarks>
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuted(PageApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuting(PageApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var handlerType = context.PageApplicationModel.HandlerType.AsType();
|
||||
|
||||
var viewDataProperties = ViewDataAttributePropertyProvider.GetViewDataProperties(handlerType);
|
||||
if (viewDataProperties == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var filter = new PageViewDataAttributeFilterFactory(viewDataProperties);
|
||||
context.PageApplicationModel.Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +203,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
//
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IApplicationModelProvider, TempDataApplicationModelProvider>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IApplicationModelProvider, ViewDataAttributeApplicationModelProvider>());
|
||||
services.TryAddSingleton<SaveTempDataFilter>();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. 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 Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
internal class ControllerViewDataAttributeFilter : IActionFilter, IViewDataValuesProviderFeature
|
||||
{
|
||||
public ControllerViewDataAttributeFilter(IReadOnlyList<LifecycleProperty> properties)
|
||||
{
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
public object Subject { get; set; }
|
||||
|
||||
public IReadOnlyList<LifecycleProperty> Properties { get; }
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
Subject = context.Controller;
|
||||
context.HttpContext.Features.Set<IViewDataValuesProviderFeature>(this);
|
||||
}
|
||||
|
||||
public void ProvideViewDataValues(ViewDataDictionary viewData)
|
||||
{
|
||||
for (var i = 0; i < Properties.Count; i++)
|
||||
{
|
||||
var property = Properties[i];
|
||||
var value = property.GetValue(Subject);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
viewData[property.Key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
internal class ControllerViewDataAttributeFilterFactory : IFilterFactory
|
||||
{
|
||||
public ControllerViewDataAttributeFilterFactory(IReadOnlyList<LifecycleProperty> properties)
|
||||
{
|
||||
Properties = properties;
|
||||
}
|
||||
|
||||
public IReadOnlyList<LifecycleProperty> Properties { get; }
|
||||
|
||||
// ControllerViewDataAttributeFilter is stateful and cannot be reused.
|
||||
public bool IsReusable => false;
|
||||
|
||||
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
|
||||
{
|
||||
return new ControllerViewDataAttributeFilter(Properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||
{
|
||||
public interface IViewDataValuesProviderFeature
|
||||
{
|
||||
void ProvideViewDataValues(ViewDataDictionary viewData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
internal class ViewDataAttributeApplicationModelProvider : IApplicationModelProvider
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <remarks>This order ensures that <see cref="ViewDataAttributeApplicationModelProvider"/> runs after the <see cref="DefaultApplicationModelProvider"/>.</remarks>
|
||||
public int Order => -1000 + 10;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuted(ApplicationModelProviderContext context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnProvidersExecuting(ApplicationModelProviderContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
foreach (var controllerModel in context.Result.Controllers)
|
||||
{
|
||||
var controllerType = controllerModel.ControllerType.AsType();
|
||||
|
||||
var viewDataProperties = ViewDataAttributePropertyProvider.GetViewDataProperties(controllerType);
|
||||
if (viewDataProperties == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var filter = new ControllerViewDataAttributeFilterFactory(viewDataProperties);
|
||||
controllerModel.Filters.Add(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||
{
|
||||
public static class ViewDataAttributePropertyProvider
|
||||
{
|
||||
public static IReadOnlyList<LifecycleProperty> GetViewDataProperties(Type type)
|
||||
{
|
||||
List<LifecycleProperty> results = null;
|
||||
|
||||
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
|
||||
|
||||
for (var i = 0; i < propertyHelpers.Length; i++)
|
||||
{
|
||||
var propertyHelper = propertyHelpers[i];
|
||||
var property = propertyHelper.Property;
|
||||
var tempDataAttribute = property.GetCustomAttribute<ViewDataAttribute>();
|
||||
if (tempDataAttribute != null)
|
||||
{
|
||||
if (results == null)
|
||||
{
|
||||
results = new List<LifecycleProperty>();
|
||||
}
|
||||
|
||||
var key = tempDataAttribute.Key;
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
key = property.Name;
|
||||
}
|
||||
|
||||
results.Add(new LifecycleProperty(property, key));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// Properties decorated with <see cref="ViewDataAttribute"/> will have their values stored in
|
||||
/// and loaded from the <see cref="ViewDataDictionary"/>. <see cref="ViewDataDictionary"/>
|
||||
/// is supported on properties of Controllers, and Razor Page handlers.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
|
||||
public sealed class ViewDataAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the key used to get or add the property from value from <see cref="ViewDataDictionary"/>.
|
||||
/// When unspecified, the key is the property name.
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -115,6 +115,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
writer,
|
||||
_htmlHelperOptions);
|
||||
|
||||
OnExecuting(viewContext);
|
||||
|
||||
// IViewComponentHelper is stateful, we want to make sure to retrieve it every time we need it.
|
||||
var viewComponentHelper = context.HttpContext.RequestServices.GetRequiredService<IViewComponentHelper>();
|
||||
(viewComponentHelper as IViewContextAware)?.Contextualize(viewContext);
|
||||
|
|
@ -124,6 +126,15 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
private void OnExecuting(ViewContext viewContext)
|
||||
{
|
||||
var viewDataValuesProvider = viewContext.HttpContext.Features.Get<IViewDataValuesProviderFeature>();
|
||||
if (viewDataValuesProvider != null)
|
||||
{
|
||||
viewDataValuesProvider.ProvideViewDataValues(viewContext.ViewData);
|
||||
}
|
||||
}
|
||||
|
||||
private Task<IHtmlContent> GetViewComponentResult(IViewComponentHelper viewComponentHelper, ILogger logger, ViewComponentResult result)
|
||||
{
|
||||
if (result.ViewComponentType == null && result.ViewComponentName == null)
|
||||
|
|
|
|||
|
|
@ -231,6 +231,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
response.StatusCode = statusCode.Value;
|
||||
}
|
||||
|
||||
OnExecuting(viewContext);
|
||||
|
||||
using (var writer = WriterFactory.CreateWriter(response.Body, resolvedContentTypeEncoding))
|
||||
{
|
||||
var view = viewContext.View;
|
||||
|
|
@ -257,5 +259,14 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
await writer.FlushAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExecuting(ViewContext viewContext)
|
||||
{
|
||||
var viewDataValuesProvider = viewContext.HttpContext.Features.Get<IViewDataValuesProviderFeature>();
|
||||
if (viewDataValuesProvider != null)
|
||||
{
|
||||
viewDataValuesProvider.ProvideViewDataValues(viewContext.ViewData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
var view = viewEngineResult.View;
|
||||
using (view as IDisposable)
|
||||
{
|
||||
|
||||
await ExecuteAsync(
|
||||
context,
|
||||
view,
|
||||
|
|
|
|||
|
|
@ -489,5 +489,36 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
// Assert
|
||||
Assert.Equal(expected, assemblyParts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewDataProperties_AreTransferredToViews()
|
||||
{
|
||||
// Act
|
||||
var document = await Client.GetHtmlDocumentAsync("ViewDataProperty/ViewDataPropertyToView");
|
||||
|
||||
// Assert
|
||||
var message = document.QuerySelector("#message").TextContent;
|
||||
Assert.Equal("Message set in action", message);
|
||||
|
||||
var filterMessage = document.QuerySelector("#filter-message").TextContent;
|
||||
Assert.Equal("Value set in OnActionExecuting", filterMessage);
|
||||
|
||||
var title = document.QuerySelector("title").TextContent;
|
||||
Assert.Equal("View Data Property Sample", title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewDataProperties_AreTransferredToViewComponents()
|
||||
{
|
||||
// Act
|
||||
var document = await Client.GetHtmlDocumentAsync("ViewDataProperty/ViewDataPropertyToViewComponent");
|
||||
|
||||
// Assert
|
||||
var message = document.QuerySelector("#message").TextContent;
|
||||
Assert.Equal("Message set in action", message);
|
||||
|
||||
var title = document.QuerySelector("title").TextContent;
|
||||
Assert.Equal("View Data Property Sample", title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. 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.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using AngleSharp.Dom.Html;
|
||||
using AngleSharp.Parser.Html;
|
||||
using Xunit;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||
{
|
||||
public static class HttpClientExtensions
|
||||
{
|
||||
public static async Task<IHtmlDocument> GetHtmlDocumentAsync(this HttpClient client, string requestUri)
|
||||
{
|
||||
var response = await client.GetAsync(requestUri);
|
||||
await AssertStatusCodeAsync(response, HttpStatusCode.OK);
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
var parser = new HtmlParser();
|
||||
var document = parser.Parse(content);
|
||||
if (document == null)
|
||||
{
|
||||
throw new InvalidOperationException("Response content could not be parsed as HTML: " + Environment.NewLine + content);
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
public static async Task<HttpResponseMessage> AssertStatusCodeAsync(this HttpResponseMessage response, HttpStatusCode expectedStatusCode)
|
||||
{
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return response;
|
||||
}
|
||||
|
||||
string responseContent = null;
|
||||
try
|
||||
{
|
||||
responseContent = await response.Content.ReadAsStringAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
throw new StatusCodeMismatchException
|
||||
{
|
||||
ExpectedStatusCode = HttpStatusCode.OK,
|
||||
ActualStatusCode = response.StatusCode,
|
||||
ResponseContent = responseContent,
|
||||
};
|
||||
}
|
||||
|
||||
private class StatusCodeMismatchException : XunitException
|
||||
{
|
||||
public HttpStatusCode ExpectedStatusCode { get; set; }
|
||||
|
||||
public HttpStatusCode ActualStatusCode { get; set; }
|
||||
|
||||
public string ResponseContent { get; set; }
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return $"Excepted status code 200. Actual {ActualStatusCode}. Response Content:" + Environment.NewLine + ResponseContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,20 +18,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<!-- For testing the testing infrastructure only -->
|
||||
<WebApplicationFactoryContentRootAttribute
|
||||
Include="BasicWebSite"
|
||||
AssemblyName="BasicWebsite"
|
||||
ContentRootPath="../../../../WebSites/BasicWebSite"
|
||||
ContentRootTest="BasicWebSite.csproj"
|
||||
Priority="-1" />
|
||||
<WebApplicationFactoryContentRootAttribute Include="BasicWebSite" AssemblyName="BasicWebsite" ContentRootPath="../../../../WebSites/BasicWebSite" ContentRootTest="BasicWebSite.csproj" Priority="-1" />
|
||||
<!-- For testing the testing infrastructure only. This attribute is
|
||||
incorrect by design to ensure that the infrastructure falls back. -->
|
||||
<WebApplicationFactoryContentRootAttribute
|
||||
Include="FiltersWebSite"
|
||||
AssemblyName="FiltersWebSite"
|
||||
ContentRootPath="/../WebSites/FiltersWebSite"
|
||||
ContentRootTest="Incorrect.csproj"
|
||||
Priority="-1" />
|
||||
<WebApplicationFactoryContentRootAttribute Include="FiltersWebSite" AssemblyName="FiltersWebSite" ContentRootPath="/../WebSites/FiltersWebSite" ContentRootTest="Incorrect.csproj" Priority="-1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -59,6 +49,7 @@
|
|||
<ProjectReference Include="..\WebSites\WebApiCompatShimWebSite\WebApiCompatShimWebSite.csproj" />
|
||||
<ProjectReference Include="..\WebSites\XmlFormattersWebSite\XmlFormattersWebSite.csproj" />
|
||||
|
||||
<PackageReference Include="AngleSharp" Version="$(AngleSharpPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.ChunkingCookieManager.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreChunkingCookieManagerSourcesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(MicrosoftAspNetCoreTestHostPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -845,7 +845,6 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPa
|
|||
Assert.Equal(expected, response.Headers.Location.ToString());
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task RedirectToSelfWorks()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -504,5 +504,53 @@ Hello from /Pages/Shared/";
|
|||
// Assert
|
||||
Assert.Contains("This page is overriden by RazorPagesWebSite", response);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewDataAttributes_SetInPageModel_AreTransferedToLayout()
|
||||
{
|
||||
// Arrange
|
||||
var document = await Client.GetHtmlDocumentAsync("/ViewData/ViewDataInPage");
|
||||
|
||||
// Assert
|
||||
var description = document.QuerySelector("meta[name='description']").Attributes["content"];
|
||||
Assert.Equal("Description set in handler", description.Value);
|
||||
|
||||
var keywords = document.QuerySelector("meta[name='keywords']").Attributes["content"];
|
||||
Assert.Equal("Value set in filter", keywords.Value);
|
||||
|
||||
var author = document.QuerySelector("meta[name='author']").Attributes["content"];
|
||||
Assert.Equal("Property with key", author.Value);
|
||||
|
||||
var title = document.QuerySelector("title").TextContent;
|
||||
Assert.Equal("Title with default value", title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewDataAttributes_SetInPageWithoutModel_AreTransferedToLayout()
|
||||
{
|
||||
// Arrange
|
||||
var document = await Client.GetHtmlDocumentAsync("/ViewData/ViewDataInPageWithoutModel");
|
||||
|
||||
// Assert
|
||||
var description = document.QuerySelector("meta[name='description']").Attributes["content"];
|
||||
Assert.Equal("Description set in page handler", description.Value);
|
||||
|
||||
var title = document.QuerySelector("title").TextContent;
|
||||
Assert.Equal("Default value", title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ViewDataProperties_SetInPageModel_AreTransferredToViewComponents()
|
||||
{
|
||||
// Act
|
||||
var document = await Client.GetHtmlDocumentAsync("ViewData/ViewDataToViewComponentPage");
|
||||
|
||||
// Assert
|
||||
var message = document.QuerySelector("#message").TextContent;
|
||||
Assert.Equal("Message set in handler", message);
|
||||
|
||||
var title = document.QuerySelector("title").TextContent;
|
||||
Assert.Equal("View Data in Pages", title);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
public class PageViewDataAttributeFilterFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateInstance_CreatesFilter()
|
||||
{
|
||||
// Arrange
|
||||
var properties = new LifecycleProperty[]
|
||||
{
|
||||
new LifecycleProperty(),
|
||||
new LifecycleProperty(),
|
||||
};
|
||||
var filterFactory = new PageViewDataAttributeFilterFactory(properties);
|
||||
|
||||
// Act
|
||||
var result = filterFactory.CreateInstance(Mock.Of<IServiceProvider>());
|
||||
|
||||
// Assert
|
||||
var filter = Assert.IsType<PageViewDataAttributeFilter>(result);
|
||||
Assert.Same(properties, filter.Properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
public class PageViewDataAttributeFilterTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnPageHandlerExecuting_AddsFeature()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new PageViewDataAttributeFilter(Array.Empty<LifecycleProperty>());
|
||||
var handler = new object();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var pageContext = new PageContext(actionContext);
|
||||
var context = new PageHandlerExecutingContext(pageContext, new IFilterMetadata[0], new HandlerMethodDescriptor(), new Dictionary<string, object>(), handler);
|
||||
|
||||
// Act
|
||||
filter.OnPageHandlerExecuting(context);
|
||||
|
||||
// Assert
|
||||
var feature = Assert.Single(httpContext.Features, f => f.Key == typeof(IViewDataValuesProviderFeature));
|
||||
Assert.Same(filter, feature.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnPageHandlerExecuting_SetsSubject()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new PageViewDataAttributeFilter(Array.Empty<LifecycleProperty>());
|
||||
var handler = new object();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var pageContext = new PageContext(actionContext);
|
||||
var context = new PageHandlerExecutingContext(pageContext, new IFilterMetadata[0], new HandlerMethodDescriptor(), new Dictionary<string, object>(), handler);
|
||||
|
||||
// Act
|
||||
filter.OnPageHandlerExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Same(handler, filter.Subject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProvideValues_AddsNonNullPropertyValuesToViewData()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestModel);
|
||||
var properties = new[]
|
||||
{
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestModel.Prop1)), "Prop1"),
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestModel.Prop2)), "Prop2"),
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestModel.Prop3)), "Prop3"),
|
||||
};
|
||||
|
||||
var controller = new TestModel();
|
||||
var filter = new PageViewDataAttributeFilter(properties)
|
||||
{
|
||||
Subject = controller,
|
||||
};
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
|
||||
// Act
|
||||
controller.Prop1 = "New-Value";
|
||||
filter.ProvideViewDataValues(viewData);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
viewData.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("Prop1", kvp.Key);
|
||||
Assert.Equal("New-Value", kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("Prop2", kvp.Key);
|
||||
Assert.Equal("Test", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
public class TestModel
|
||||
{
|
||||
public string Prop1 { get; set; }
|
||||
|
||||
public string Prop2 => "Test";
|
||||
|
||||
public string Prop3 { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) .NET Foundation. 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.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public class ViewDataAttributePageApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DoesNotAddFilter_IfTypeHasNoViewDataProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestPageModel_NoViewDataProperties);
|
||||
var provider = new ViewDataAttributePageApplicationModelProvider();
|
||||
var context = CreateProviderContext(type);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(context.PageApplicationModel.Filters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddsViewDataPropertyFilter_ForViewDataAttributeProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestPageModel_ViewDataProperties);
|
||||
var provider = new ViewDataAttributePageApplicationModelProvider();
|
||||
var context = CreateProviderContext(type);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var filter = Assert.Single(context.PageApplicationModel.Filters);
|
||||
var viewDataFilter = Assert.IsType<PageViewDataAttributeFilterFactory>(filter);
|
||||
Assert.Collection(
|
||||
viewDataFilter.Properties,
|
||||
property => Assert.Equal(nameof(TestPageModel_ViewDataProperties.DateTime), property.PropertyInfo.Name));
|
||||
}
|
||||
|
||||
private static PageApplicationModelProviderContext CreateProviderContext(Type handlerType)
|
||||
{
|
||||
var descriptor = new CompiledPageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeof(TestPage).GetTypeInfo())
|
||||
{
|
||||
PageApplicationModel = new PageApplicationModel(descriptor, handlerType.GetTypeInfo(), Array.Empty<object>()),
|
||||
};
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
private class TestPage : Page
|
||||
{
|
||||
public object Model => null;
|
||||
|
||||
public override Task ExecuteAsync() => null;
|
||||
}
|
||||
|
||||
public class TestPageModel_NoViewDataProperties
|
||||
{
|
||||
public DateTime? DateTime { get; set; }
|
||||
}
|
||||
|
||||
public class TestPageModel_ViewDataProperties
|
||||
{
|
||||
[ViewData]
|
||||
public DateTime? DateTime { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,6 +442,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
typeof(CorsApplicationModelProvider),
|
||||
typeof(AuthorizationApplicationModelProvider),
|
||||
typeof(TempDataApplicationModelProvider),
|
||||
typeof(ViewDataAttributeApplicationModelProvider),
|
||||
typeof(ApiBehaviorApplicationModelProvider),
|
||||
}
|
||||
},
|
||||
|
|
@ -469,6 +470,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
typeof(AuthorizationPageApplicationModelProvider),
|
||||
typeof(DefaultPageApplicationModelProvider),
|
||||
typeof(TempDataFilterPageApplicationModelProvider),
|
||||
typeof(ViewDataAttributePageApplicationModelProvider),
|
||||
typeof(ResponseCacheFilterApplicationModelProvider),
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
public class ControllerViewDataAttributeFilterFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateInstance_CreatesFilter()
|
||||
{
|
||||
// Arrange
|
||||
var properties = new LifecycleProperty[]
|
||||
{
|
||||
new LifecycleProperty(),
|
||||
new LifecycleProperty(),
|
||||
};
|
||||
var filterFactory = new ControllerViewDataAttributeFilterFactory(properties);
|
||||
|
||||
// Act
|
||||
var result = filterFactory.CreateInstance(Mock.Of<IServiceProvider>());
|
||||
|
||||
// Assert
|
||||
var filter = Assert.IsType<ControllerViewDataAttributeFilter>(result);
|
||||
Assert.Same(properties, filter.Properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) .NET Foundation. 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
public class ControllerViewDataAttributeFilterTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnActionExecuting_AddsFeature()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new ControllerViewDataAttributeFilter(Array.Empty<LifecycleProperty>());
|
||||
var controller = new object();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var context = new ActionExecutingContext(actionContext, new IFilterMetadata[0], new Dictionary<string, object>(), controller);
|
||||
|
||||
// Act
|
||||
filter.OnActionExecuting(context);
|
||||
|
||||
// Assert
|
||||
var feature = Assert.Single(httpContext.Features, f => f.Key == typeof(IViewDataValuesProviderFeature));
|
||||
Assert.Same(filter, feature.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnActionExecuting_SetsSubject()
|
||||
{
|
||||
// Arrange
|
||||
var filter = new ControllerViewDataAttributeFilter(Array.Empty<LifecycleProperty>());
|
||||
var controller = new object();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var context = new ActionExecutingContext(actionContext, new IFilterMetadata[0], new Dictionary<string, object>(), controller);
|
||||
|
||||
// Act
|
||||
filter.OnActionExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.Same(controller, filter.Subject);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProvideValues_AddsNonNullPropertyValuesToViewData()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestController);
|
||||
var properties = new[]
|
||||
{
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestController.Prop1)), "Prop1"),
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestController.Prop2)), "Prop2"),
|
||||
new LifecycleProperty(type.GetProperty(nameof(TestController.Prop3)), "Prop3"),
|
||||
};
|
||||
|
||||
var controller = new TestController();
|
||||
var filter = new ControllerViewDataAttributeFilter(properties)
|
||||
{
|
||||
Subject = controller,
|
||||
};
|
||||
var viewData = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary());
|
||||
|
||||
// Act
|
||||
controller.Prop1 = "New-Value";
|
||||
filter.ProvideViewDataValues(viewData);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
viewData.OrderBy(kvp => kvp.Key),
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("Prop1", kvp.Key);
|
||||
Assert.Equal("New-Value", kvp.Value);
|
||||
},
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("Prop2", kvp.Key);
|
||||
Assert.Equal("Test", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
public class TestController
|
||||
{
|
||||
public string Prop1 { get; set; }
|
||||
|
||||
public string Prop2 => "Test";
|
||||
|
||||
public string Prop3 { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) .NET Foundation. 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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||
{
|
||||
public class ViewDataAttributeApplicationModelProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DoesNotAddFilter_IfTypeHasNoViewDataProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestController_NoViewDataProperties);
|
||||
var provider = new ViewDataAttributeApplicationModelProvider();
|
||||
var context = GetContext(type);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var controller = Assert.Single(context.Result.Controllers);
|
||||
Assert.Empty(controller.Filters);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddsViewDataPropertyFilter_ForViewDataAttributeProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestController_NullableNonPrimitiveViewDataProperty);
|
||||
var provider = new ViewDataAttributeApplicationModelProvider();
|
||||
var context = GetContext(type);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var controller = Assert.Single(context.Result.Controllers);
|
||||
Assert.IsType<ControllerViewDataAttributeFilterFactory>(Assert.Single(controller.Filters));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InitializeFilterFactory_WithExpectedPropertyHelpers_ForViewDataAttributeProperties()
|
||||
{
|
||||
// Arrange
|
||||
var expected = typeof(TestController_OneViewDataProperty).GetProperty(nameof(TestController_OneViewDataProperty.Test2));
|
||||
var provider = new ViewDataAttributeApplicationModelProvider();
|
||||
var context = GetContext(typeof(TestController_OneViewDataProperty));
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
var controller = context.Result.Controllers.SingleOrDefault();
|
||||
var filter = Assert.IsType<ControllerViewDataAttributeFilterFactory>(Assert.Single(controller.Filters));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(filter);
|
||||
var property = Assert.Single(filter.Properties);
|
||||
Assert.Same(expected, property.PropertyInfo);
|
||||
Assert.Equal("Test2", property.Key);
|
||||
}
|
||||
|
||||
private static ApplicationModelProviderContext GetContext(Type type)
|
||||
{
|
||||
var defaultProvider = new DefaultApplicationModelProvider(
|
||||
Options.Create(new MvcOptions()),
|
||||
new EmptyModelMetadataProvider());
|
||||
|
||||
var context = new ApplicationModelProviderContext(new[] { type.GetTypeInfo() });
|
||||
defaultProvider.OnProvidersExecuting(context);
|
||||
return context;
|
||||
}
|
||||
|
||||
public class TestController_NoViewDataProperties
|
||||
{
|
||||
public DateTime? DateTime { get; set; }
|
||||
}
|
||||
|
||||
public class TestController_NullableNonPrimitiveViewDataProperty
|
||||
{
|
||||
[ViewData]
|
||||
public DateTime? DateTime { get; set; }
|
||||
}
|
||||
|
||||
public class TestController_OneViewDataProperty
|
||||
{
|
||||
public string Test { get; set; }
|
||||
|
||||
[ViewData]
|
||||
public string Test2 { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright (c) .NET Foundation. 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||
{
|
||||
public class ViewDataAttributePropertyProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetViewDataProperties_ReturnsNull_IfTypeDoesNotHaveAnyViewDataProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(TestController_NoViewDataProperties);
|
||||
|
||||
// Act
|
||||
var result = ViewDataAttributePropertyProvider.GetViewDataProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetViewDataProperties_ReturnsViewDataProperties()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(BaseController);
|
||||
|
||||
// Act
|
||||
var result = ViewDataAttributePropertyProvider.GetViewDataProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result.OrderBy(p => p.Key),
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(BaseController.BaseProperty), property.PropertyInfo.Name);
|
||||
Assert.Equal(nameof(BaseController.BaseProperty), property.Key);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetViewDataProperties_ReturnsViewDataProperties_FromBaseTypes()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(DerivedController);
|
||||
|
||||
// Act
|
||||
var result = ViewDataAttributePropertyProvider.GetViewDataProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result.OrderBy(p => p.Key),
|
||||
property => Assert.Equal(nameof(BaseController.BaseProperty), property.PropertyInfo.Name),
|
||||
property => Assert.Equal(nameof(DerivedController.DeriviedProperty), property.PropertyInfo.Name));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetViewDataProperties_UsesKeyFromViewDataAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var type = typeof(PropertyWithKeyController);
|
||||
|
||||
// Act
|
||||
var result = ViewDataAttributePropertyProvider.GetViewDataProperties(type);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
result.OrderBy(p => p.Key),
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(PropertyWithKeyController.Different), property.PropertyInfo.Name);
|
||||
Assert.Equal("Test", property.Key);
|
||||
});
|
||||
}
|
||||
|
||||
public class TestController_NoViewDataProperties
|
||||
{
|
||||
public DateTime? DateTime { get; set; }
|
||||
}
|
||||
|
||||
public class BaseController
|
||||
{
|
||||
[ViewData]
|
||||
public string BaseProperty { get; }
|
||||
}
|
||||
|
||||
public class DerivedController : BaseController
|
||||
{
|
||||
[ViewData]
|
||||
public string DeriviedProperty { get; set; }
|
||||
}
|
||||
|
||||
public class PropertyWithKeyController
|
||||
{
|
||||
[ViewData(Key = "Test")]
|
||||
public string Different { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace HtmlGenerationWebSite.Components
|
||||
namespace BasicWebSite.Components
|
||||
{
|
||||
public class PassThroughViewComponent : ViewComponent
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BasicWebSite.Components
|
||||
{
|
||||
public class ViewDataViewComponent : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke() => View();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using HtmlGenerationWebSite.Components;
|
||||
using BasicWebSite.Components;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace BasicWebSite.Controllers
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BasicWebSite.Components;
|
||||
using BasicWebSite.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace BasicWebSite.Controllers
|
||||
{
|
||||
public class ViewDataPropertyController : Controller
|
||||
{
|
||||
[ViewData]
|
||||
public string Title => "View Data Property Sample";
|
||||
|
||||
[ViewData]
|
||||
public string Message { get; private set; }
|
||||
|
||||
[ViewData]
|
||||
public string FilterMessage { get; set; }
|
||||
|
||||
public IActionResult ViewDataPropertyToView()
|
||||
{
|
||||
Message = "Message set in action";
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult ViewDataPropertyToViewComponent()
|
||||
{
|
||||
Message = "Message set in action";
|
||||
return ViewComponent(typeof(ViewDataViewComponent));
|
||||
}
|
||||
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
FilterMessage = "Value set in OnActionExecuting";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
<span id="message">@ViewData["Message"]</span>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
Sample that shows value of ViewDataAttribute being passed to a ViewComponent
|
||||
<span class="view-data">@ViewData["Message"]</span>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@{ Layout = "_Layout"; }
|
||||
Sample that shows ViewDataAttribute being applied to a controller
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
<span id="message">@ViewData["Message"]</span>
|
||||
<span id="filter-message">@ViewData["FilterMessage"]</span>
|
||||
@RenderBody()
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace RazorPagesWebSite.Components
|
||||
{
|
||||
public class ViewDataViewComponent : ViewComponent
|
||||
{
|
||||
public IViewComponentResult Invoke()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
<span id="message">@ViewData["Message"]</span>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
public class ViewDataInPage : PageModel
|
||||
{
|
||||
[ViewData]
|
||||
public string Title => "Title with default value";
|
||||
|
||||
[ViewData]
|
||||
public string Keywords { get; set; }
|
||||
|
||||
[ViewData]
|
||||
public string Description { get; set;}
|
||||
|
||||
[ViewData(Key = "Author")]
|
||||
public string AuthorName { get; set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Description = "Description set in handler";
|
||||
AuthorName = "Property with key";
|
||||
}
|
||||
|
||||
public override void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
Keywords = "Value set in filter";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
@page
|
||||
@model ViewDataInPage
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
Sample that shows ViewData attributes being set in a PageModel.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
@page
|
||||
@{
|
||||
Layout = "_Layout";
|
||||
}
|
||||
@functions
|
||||
{
|
||||
[ViewData]
|
||||
public string Title { get; set; } = "Default value";
|
||||
|
||||
[ViewData]
|
||||
public string Description { get; set; }
|
||||
|
||||
public void OnGet()
|
||||
{
|
||||
Description = "Description set in page handler";
|
||||
}
|
||||
}
|
||||
Sample that shows ViewData being set from a page without handler.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using RazorPagesWebSite.Components;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
public class ViewDataToViewComponentPage : PageModel
|
||||
{
|
||||
[ViewData]
|
||||
public string Title => "View Data in Pages";
|
||||
|
||||
[ViewData]
|
||||
public string Message { get; private set; }
|
||||
|
||||
public IActionResult OnGet()
|
||||
{
|
||||
Message = "Message set in handler";
|
||||
return new ViewComponentResult
|
||||
{
|
||||
ViewComponentType = typeof(ViewDataViewComponent),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
@page
|
||||
@model ViewDataToViewComponentPage
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta name="description" content="@ViewData["Description"]" />
|
||||
<meta name="keywords" content="@ViewData["Keywords"]" />
|
||||
<meta name="author" content="@ViewData["Author"]" />
|
||||
<title>@ViewData["Title"]</title>
|
||||
</head>
|
||||
<body>
|
||||
@RenderBody()
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1 @@
|
|||
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
|
||||
Loading…
Reference in New Issue