Ensure ViewData is set on PageResult after a handler method executes

[Fixes #7665] Initializing PageResult.Page as part of result execution breaks Identity UI
This commit is contained in:
Kiran Challa 2018-04-17 12:09:42 -07:00
parent 4f7d53f4e7
commit faca3da42f
5 changed files with 89 additions and 1 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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<IPageFilter>();
AllowSelector(pageFilter);
pageFilter
.Setup(f => f.OnPageHandlerExecuted(It.IsAny<PageHandlerExecutedContext>()))
.Callback<PageHandlerExecutedContext>(c =>
{
pageHandlerExecutedCalled = true;
var result = c.Result;
var pageResult = Assert.IsType<PageResult>(result);
Assert.IsType<ViewDataDictionary<TestPage>>(pageResult.ViewData);
Assert.IsType<TestPage>(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()
{

View File

@ -0,0 +1,5 @@
@page
@model ViewDataAvailableAfterHandlerExecutedModel
@{
}
ViewData: @ViewData["Foo"]

View File

@ -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)
{
}
}
}
}