Combine attributes specified on Page and PageModel (#12332)
Fixes https://github.com/aspnet/AspNetCore/issues/11296
This commit is contained in:
parent
b23ea5b668
commit
a77738e52f
|
|
@ -2,9 +2,7 @@
|
|||
// 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 System.Text;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
// 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 System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
|
@ -98,16 +98,23 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
|
||||
// Now we want figure out which type is the handler type.
|
||||
TypeInfo handlerType;
|
||||
var pageTypeAttributes = pageTypeInfo.GetCustomAttributes(inherit: true);
|
||||
object[] handlerTypeAttributes;
|
||||
if (modelProperty.PropertyType.IsDefined(typeof(PageModelAttribute), inherit: true))
|
||||
{
|
||||
handlerType = modelTypeInfo;
|
||||
|
||||
// If a PageModel is specified, combine the attributes specified on the Page and the Model type.
|
||||
// Attributes that appear earlier in the are more significant. In this case, we'll treat attributes on the model (code)
|
||||
// to be more signficant than the page (code-generated).
|
||||
handlerTypeAttributes = modelTypeInfo.GetCustomAttributes(inherit: true).Concat(pageTypeAttributes).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
handlerType = pageTypeInfo;
|
||||
handlerTypeAttributes = pageTypeInfo.GetCustomAttributes(inherit: true);
|
||||
}
|
||||
|
||||
var handlerTypeAttributes = handlerType.GetCustomAttributes(inherit: true);
|
||||
var pageModel = new PageApplicationModel(
|
||||
actionDescriptor,
|
||||
declaredModelType,
|
||||
|
|
|
|||
|
|
@ -525,6 +525,43 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CombinesFilters_OnPageAndPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var provider = CreateProvider();
|
||||
var typeInfo = typeof(PageWithFilters).GetTypeInfo();
|
||||
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
var pageModel = context.PageApplicationModel;
|
||||
Assert.Collection(
|
||||
pageModel.Filters,
|
||||
filter => Assert.IsType<TypeFilterAttribute>(filter),
|
||||
filter => Assert.IsType<ServiceFilterAttribute>(filter),
|
||||
filter => Assert.IsType<PageHandlerPageFilter>(filter),
|
||||
filter => Assert.IsType<HandleOptionsRequestsPageFilter>(filter));
|
||||
}
|
||||
|
||||
[ServiceFilter(typeof(Guid))]
|
||||
private class PageWithFilters : Page
|
||||
{
|
||||
public PageWithFilterModel Model { get; }
|
||||
|
||||
public override Task ExecuteAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
[TypeFilter(typeof(string))]
|
||||
private class PageWithFilterModel : PageModel
|
||||
{
|
||||
}
|
||||
|
||||
[ServiceFilter(typeof(IServiceProvider))]
|
||||
private class FiltersOnPageAndPageModel : PageModel { }
|
||||
|
||||
[Fact] // If the model has handler methods, we prefer those.
|
||||
public void CreateDescriptor_FindsHandlerMethod_OnModel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -695,6 +695,40 @@ Hello from /Pages/Shared/";
|
|||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthAttribute_AppliedOnPageWorks()
|
||||
{
|
||||
// Act
|
||||
using var response = await Client.GetAsync("/Filters/AuthFilterOnPage");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("/Login?ReturnUrl=%2FFilters%2FAuthFilterOnPage", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AuthAttribute_AppliedOnPageWithModelWorks()
|
||||
{
|
||||
// Act
|
||||
using var response = await Client.GetAsync("/Filters/AuthFilterOnPageWithModel");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
|
||||
Assert.Equal("/Login?ReturnUrl=%2FFilters%2FAuthFilterOnPageWithModel", response.Headers.Location.PathAndQuery);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task FiltersAppliedToPageAndPageModelAreExecuted()
|
||||
{
|
||||
// Act
|
||||
using var response = await Client.GetAsync("/Filters/FiltersAppliedToPageAndPageModel");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal(new[] { "PageModelFilterValue" }, response.Headers.GetValues("PageModelFilterKey"));
|
||||
Assert.Equal(new[] { "PageFilterValue" }, response.Headers.GetValues("PageFilterKey"));
|
||||
}
|
||||
|
||||
private async Task AddAntiforgeryHeadersAsync(HttpRequestMessage request)
|
||||
{
|
||||
var response = await Client.GetAsync(request.RequestUri);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
@page
|
||||
|
||||
@attribute [Authorize]
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite.Pages.Filters
|
||||
{
|
||||
[SkipStatusCodePages]
|
||||
public class AuthFilterOnPageWithModel : PageModel
|
||||
{
|
||||
public IActionResult OnGet() => Page();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@page
|
||||
@model AuthFilterOnPageWithModel
|
||||
@attribute [Authorize]
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite.Pages.Filters
|
||||
{
|
||||
[TestPageModelFilter]
|
||||
public class FiltersAppliedToPageAndPageModel : PageModel
|
||||
{
|
||||
public IActionResult OnGet() => Page();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
@page
|
||||
@model FiltersAppliedToPageAndPageModel
|
||||
@attribute [TestPageFilter]
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.Filters;
|
||||
|
||||
namespace RazorPagesWebSite.Pages.Filters
|
||||
{
|
||||
public class TestPageFilter : Attribute, IPageFilter
|
||||
{
|
||||
public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
|
||||
{
|
||||
context.HttpContext.Response.Headers["PageFilterKey"] = "PageFilterValue";
|
||||
}
|
||||
|
||||
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// 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.Filters;
|
||||
|
||||
namespace RazorPagesWebSite.Pages.Filters
|
||||
{
|
||||
public class TestPageModelFilter : Attribute, IResourceFilter
|
||||
{
|
||||
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||
{
|
||||
context.HttpContext.Response.Headers["PageModelFilterKey"] = "PageModelFilterValue";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
@using Microsoft.AspNetCore.Authorization
|
||||
Loading…
Reference in New Issue