parent
63397653fa
commit
de38922601
|
|
@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public class BindPropertyAttribute : Attribute, IModelNameProvider, IBinderTypeProviderMetadata, IRequestPredicateProvider
|
||||
{
|
||||
private static readonly Func<ActionContext, bool> _supportsAllRequests = (c) => true;
|
||||
|
|
@ -31,22 +31,14 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
return _bindingSource;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_bindingSource = value;
|
||||
}
|
||||
protected set => _bindingSource = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name { get; set; }
|
||||
|
||||
Func<ActionContext, bool> IRequestPredicateProvider.RequestPredicate
|
||||
{
|
||||
get
|
||||
{
|
||||
return SupportsGet ? _supportsAllRequests : _supportsNonGetRequests;
|
||||
}
|
||||
}
|
||||
=> SupportsGet ? _supportsAllRequests : _supportsNonGetRequests;
|
||||
|
||||
private static bool IsNonGetRequest(ActionContext context)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -224,7 +224,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
return propertyModel;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="ActionModel"/> instance for the given action <see cref="MethodInfo"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -220,10 +220,15 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
throw new ArgumentNullException(nameof(property));
|
||||
}
|
||||
|
||||
var attributes = property.GetCustomAttributes(inherit: true);
|
||||
var bindingInfo = BindingInfo.GetBindingInfo(attributes);
|
||||
var propertyAttributes = property.GetCustomAttributes(inherit: true);
|
||||
var handlerAttributes = property.DeclaringType.GetCustomAttributes(inherit: true);
|
||||
|
||||
var model = new PagePropertyModel(property, property.GetCustomAttributes(inherit: true))
|
||||
// Look for binding info on the handler if nothing is specified on the property.
|
||||
// This allows BindProperty attributes on handlers to apply to properties.
|
||||
var bindingInfo = BindingInfo.GetBindingInfo(propertyAttributes) ??
|
||||
BindingInfo.GetBindingInfo(handlerAttributes);
|
||||
|
||||
var model = new PagePropertyModel(property, propertyAttributes)
|
||||
{
|
||||
PropertyName = property.Name,
|
||||
BindingInfo = bindingInfo,
|
||||
|
|
|
|||
|
|
@ -1136,9 +1136,59 @@ Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary`1[AspNetCore._InjectedP
|
|||
// Act
|
||||
var response = await Client.GetStringAsync("/Pages/Localized/PageWithModel?culture=fr-FR");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, response.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindPropertyAttribute_CanBeAppliedToModelType()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "Property1 = 123, Property2 = 25,";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "/Pages/PropertyBinding/BindPropertyOnModel?Property1=123")
|
||||
{
|
||||
Content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Property2", "25"),
|
||||
}),
|
||||
};
|
||||
await AddAntiforgeryHeaders(request);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
Assert.StartsWith(expected, responseContent.Trim());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BindingInfoOnPropertiesIsPreferredToBindingInfoOnType()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "Property1 = 123, Property2 = 25,";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, "/Pages/PropertyBinding/BindPropertyOnModel?Property1=123")
|
||||
{
|
||||
Content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
// FormValueProvider appears before QueryStringValueProvider. However, the FromQuery explicitly listed
|
||||
// on the property should cause it to use the latter.
|
||||
new KeyValuePair<string, string>("Property1", "345"),
|
||||
new KeyValuePair<string, string>("Property2", "25"),
|
||||
}),
|
||||
};
|
||||
await AddAntiforgeryHeaders(request);
|
||||
|
||||
// Act
|
||||
var response = await Client.SendAsync(request);
|
||||
|
||||
// Assert
|
||||
response.EnsureSuccessStatusCode();
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
Assert.StartsWith(expected, responseContent.Trim());
|
||||
}
|
||||
|
||||
private async Task AddAntiforgeryHeaders(HttpRequestMessage request)
|
||||
{
|
||||
var getResponse = await Client.GetAsync(request.RequestUri);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
|
|
@ -229,6 +228,60 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
public string Property { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversProperties_FromAllSubTypesThatDeclaresBindProperty()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DefaultPageApplicationModelProvider();
|
||||
var typeInfo = typeof(BindPropertyAttributeOnBaseModelPage).GetTypeInfo();
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerProperties.OrderBy(p => p.PropertyName).Where(p => p.BindingInfo != null),
|
||||
property =>
|
||||
{
|
||||
var name = nameof(ModelLevel3.Property2);
|
||||
Assert.Equal(typeof(ModelLevel3).GetProperty(name), property.PropertyInfo);
|
||||
Assert.Equal(name, property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
var name = nameof(ModelLevel3.Property3);
|
||||
Assert.Equal(typeof(ModelLevel3).GetProperty(name), property.PropertyInfo);
|
||||
Assert.Equal(name, property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
});
|
||||
}
|
||||
|
||||
private class BindPropertyAttributeOnBaseModelPage : Page
|
||||
{
|
||||
public ModelLevel3 Model => null;
|
||||
public override Task ExecuteAsync() => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private class ModelLevel1 : PageModel
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
private class ModelLevel2 : ModelLevel1
|
||||
{
|
||||
public string Property2 { get; set; }
|
||||
}
|
||||
|
||||
private class ModelLevel3 : ModelLevel2
|
||||
{
|
||||
public string Property3 { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversHandlersFromPage()
|
||||
{
|
||||
|
|
@ -305,6 +358,53 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversBindingInfoFromHandler()
|
||||
{
|
||||
// Arrange
|
||||
var provider = new DefaultPageApplicationModelProvider();
|
||||
var typeInfo = typeof(PageWithBindPropertyModel).GetTypeInfo();
|
||||
var modelType = typeof(ModelWithBindProperty);
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var context = new PageApplicationModelProviderContext(descriptor, typeInfo);
|
||||
|
||||
// Act
|
||||
provider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.PageApplicationModel);
|
||||
Assert.Collection(
|
||||
context.PageApplicationModel.HandlerProperties.OrderBy(p => p.PropertyName),
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(ModelWithBindProperty.Property1), property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
},
|
||||
property =>
|
||||
{
|
||||
Assert.Equal(nameof(ModelWithBindProperty.Property2), property.PropertyName);
|
||||
Assert.NotNull(property.BindingInfo);
|
||||
Assert.Equal(BindingSource.Path, property.BindingInfo.BindingSource);
|
||||
});
|
||||
}
|
||||
|
||||
private class PageWithBindPropertyModel : PageBase
|
||||
{
|
||||
public ModelWithBindProperty Model => null;
|
||||
|
||||
public override Task ExecuteAsync() => null;
|
||||
}
|
||||
|
||||
[BindProperty]
|
||||
[PageModel]
|
||||
private class ModelWithBindProperty
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
|
||||
[FromRoute]
|
||||
public string Property2 { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_DiscoversHandlersFromModel()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// 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.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
|
||||
namespace RazorPagesWebSite
|
||||
{
|
||||
[BindProperty]
|
||||
public class BindPropertyOnModel : PageModel
|
||||
{
|
||||
[FromQuery]
|
||||
public string Property1 { get; set; }
|
||||
|
||||
public string Property2 { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
@page
|
||||
@model BindPropertyOnModel
|
||||
|
||||
Property1 = @Model.Property1, Property2 = @Model.Property2,
|
||||
|
||||
<form action="">
|
||||
@Html.AntiForgeryToken()
|
||||
</form>
|
||||
Loading…
Reference in New Issue