diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs index cd21e58430..9684c711f0 100644 --- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs +++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/PageActionInvoker.cs @@ -128,7 +128,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { _page = (PageBase)CacheEntry.PageFactory(_pageContext, _viewContext); } - pageResult.Page = _page; pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData; } @@ -278,6 +277,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal { _result = new PageResult(); } + + // Ensure ViewData is set on PageResult for backwards compatibility (For example, Identity UI accesses + // ViewData in a PageFilter's PageHandlerExecutedMethod) + if (_result is PageResult pageResult) + { + pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData; + } } private Task Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) diff --git a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs index 1a0cdb4ac7..9076b9d344 100644 --- a/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.FunctionalTests/RazorPagesTest.cs @@ -1295,6 +1295,16 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore.InjectedPa Assert.Equal("From ShortCircuitPageAtPageFilter.cshtml", content); } + [Fact] + public async Task ViewDataAvaialableInPageFilter_AfterHandlerMethod_ReturnsPageResult() + { + // Act + var content = await Client.GetStringAsync("http://localhost/Pages/ViewDataAvailableAfterHandlerExecuted"); + + // Assert + Assert.Equal("ViewData: Bar", content); + } + private async Task AddAntiforgeryHeaders(HttpRequestMessage request) { var getResponse = await Client.GetAsync(request.RequestUri); diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs index f90da88615..5b7899ac3e 100644 --- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/PageActionInvokerTest.cs @@ -473,6 +473,33 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal #region Page Filters + [Fact] + public async Task ViewDataIsSet_AfterHandlerMethodIsExecuted() + { + // Arrange + var pageHandlerExecutedCalled = false; + var pageFilter = new Mock(); + AllowSelector(pageFilter); + pageFilter + .Setup(f => f.OnPageHandlerExecuted(It.IsAny())) + .Callback(c => + { + pageHandlerExecutedCalled = true; + var result = c.Result; + var pageResult = Assert.IsType(result); + Assert.IsType>(pageResult.ViewData); + Assert.IsType(pageResult.Model); + Assert.Null(pageResult.Page); + }); + var invoker = CreateInvoker(new IFilterMetadata[] { pageFilter.Object }, result: new PageResult()); + + // Act + await invoker.InvokeAsync(); + + // Assert + Assert.True(pageHandlerExecutedCalled); + } + [Fact] public async Task InvokeAction_InvokesPageFilter() { diff --git a/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml b/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml new file mode 100644 index 0000000000..a9c5e2d2e6 --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml @@ -0,0 +1,5 @@ +@page +@model ViewDataAvailableAfterHandlerExecutedModel +@{ +} +ViewData: @ViewData["Foo"] \ No newline at end of file diff --git a/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml.cs b/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml.cs new file mode 100644 index 0000000000..548623d5f9 --- /dev/null +++ b/test/WebSites/RazorPagesWebSite/Pages/ViewDataAvailableAfterHandlerExecuted.cshtml.cs @@ -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 System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace RazorPagesWebSite.Pages +{ + [TestPageFilter] + public class ViewDataAvailableAfterHandlerExecutedModel : PageModel + { + public IActionResult OnGet() + { + return Page(); + } + + private class TestPageFilterAttribute : Attribute, IPageFilter + { + public void OnPageHandlerExecuted(PageHandlerExecutedContext context) + { + // This usage mimics Identity UI where it sets data into ViewData in a PageFilters's + // PageHandlerExecuted method. + if (context.Result is PageResult pageResult) + { + pageResult.ViewData["Foo"] = "Bar"; + } + } + + public void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + } + + public void OnPageHandlerSelected(PageHandlerSelectedContext context) + { + } + } + } +}