Add `HtmlAttributeNotBoundAttribute`
- #182 - ignore otherwise-bound (i.e. `public`) properties in tag helpers nits: - add more `TagHelperDescriptorFactory` tests e.g. of `internal` properties - remove `private` setter from `HtmlAttributeNameAttribute` - use `null` propagation to shorten `IsAccessibleProperty` expression - clean up some trailing whitespace
This commit is contained in:
parent
dc4ee8b915
commit
de95f67400
|
|
@ -28,6 +28,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// HTML attribute name of the associated property.
|
/// HTML attribute name of the associated property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; private set; }
|
public string Name { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates the associated <see cref="ITagHelper"/> property should not be bound to HTML attributes.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
|
||||||
|
public sealed class HtmlAttributeNotBoundAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiates a new instance of the <see cref="HtmlAttributeNotBoundAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public HtmlAttributeNotBoundAttribute()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -203,7 +203,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(
|
private static IEnumerable<TagHelperAttributeDescriptor> GetAttributeDescriptors(
|
||||||
Type type,
|
Type type,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
|
var accessibleProperties = type.GetRuntimeProperties().Where(IsAccessibleProperty);
|
||||||
|
|
@ -226,12 +226,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
Type parentType,
|
Type parentType,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
// data-* attributes are explicitly not implemented by user agents and are not intended for use on
|
// data-* attributes are explicitly not implemented by user agents and are not intended for use on
|
||||||
// the server; therefore it's invalid for TagHelpers to bind to them.
|
// the server; therefore it's invalid for TagHelpers to bind to them.
|
||||||
if (attributeDescriptor.Name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
if (attributeDescriptor.Name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
errorSink.OnError(
|
errorSink.OnError(
|
||||||
SourceLocation.Zero,
|
SourceLocation.Zero,
|
||||||
Resources.FormatTagHelperDescriptorFactory_InvalidBoundAttributeName(
|
Resources.FormatTagHelperDescriptorFactory_InvalidBoundAttributeName(
|
||||||
attributeDescriptor.PropertyName,
|
attributeDescriptor.PropertyName,
|
||||||
parentType.FullName,
|
parentType.FullName,
|
||||||
|
|
@ -255,10 +255,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
|
|
||||||
private static bool IsAccessibleProperty(PropertyInfo property)
|
private static bool IsAccessibleProperty(PropertyInfo property)
|
||||||
{
|
{
|
||||||
return property.GetMethod != null &&
|
// Accessible properties are those with public getters and setters and without [HtmlAttributeNotBound].
|
||||||
property.GetMethod.IsPublic &&
|
return property.GetMethod?.IsPublic == true &&
|
||||||
property.SetMethod != null &&
|
property.SetMethod?.IsPublic == true &&
|
||||||
property.SetMethod.IsPublic;
|
property.GetCustomAttribute<HtmlAttributeNotBoundAttribute>(inherit: false) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,13 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
public string InvalidNoSetAttribute { get { return string.Empty; } }
|
public string InvalidNoSetAttribute { get { return string.Empty; } }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PrivateAccessorTagHelper : TagHelper
|
public class NonPublicAccessorTagHelper : TagHelper
|
||||||
{
|
{
|
||||||
public string ValidAttribute { get; set; }
|
public string ValidAttribute { get; set; }
|
||||||
public string InvalidPrivateSetAttribute { get; private set; }
|
public string InvalidPrivateSetAttribute { get; private set; }
|
||||||
public string InvalidPrivateGetAttribute { private get; set; }
|
public string InvalidPrivateGetAttribute { private get; set; }
|
||||||
|
protected string InvalidProtectedAttribute { get; set; }
|
||||||
|
internal string InvalidInternalAttribute { get; set; }
|
||||||
|
protected internal string InvalidProtectedInternalAttribute { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -443,11 +443,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var errorSink = new ErrorSink();
|
var errorSink = new ErrorSink();
|
||||||
var validProperty = typeof(PrivateAccessorTagHelper).GetProperty(
|
var validProperty = typeof(NonPublicAccessorTagHelper).GetProperty(
|
||||||
nameof(PrivateAccessorTagHelper.ValidAttribute));
|
nameof(NonPublicAccessorTagHelper.ValidAttribute));
|
||||||
var expectedDescriptor = new TagHelperDescriptor(
|
var expectedDescriptor = new TagHelperDescriptor(
|
||||||
"private-accessor",
|
"non-public-accessor",
|
||||||
typeof(PrivateAccessorTagHelper).FullName,
|
typeof(NonPublicAccessorTagHelper).FullName,
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
new[] {
|
new[] {
|
||||||
new TagHelperAttributeDescriptor(
|
new TagHelperAttributeDescriptor(
|
||||||
|
|
@ -457,7 +457,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
// Act
|
// Act
|
||||||
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||||
AssemblyName,
|
AssemblyName,
|
||||||
typeof(PrivateAccessorTagHelper),
|
typeof(NonPublicAccessorTagHelper),
|
||||||
errorSink);
|
errorSink);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -466,6 +466,51 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateDescriptor_DoesNotIncludePropertiesWithNotBound()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var errorSink = new ErrorSink();
|
||||||
|
var boundProperty = typeof(NotBoundAttributeTagHelper).GetProperty(
|
||||||
|
nameof(NotBoundAttributeTagHelper.BoundProperty));
|
||||||
|
var expectedDescriptor = new TagHelperDescriptor(
|
||||||
|
"not-bound-attribute",
|
||||||
|
typeof(NotBoundAttributeTagHelper).FullName,
|
||||||
|
AssemblyName,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new TagHelperAttributeDescriptor("bound-property", boundProperty)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||||
|
AssemblyName,
|
||||||
|
typeof(NotBoundAttributeTagHelper),
|
||||||
|
errorSink);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(errorSink.Errors);
|
||||||
|
var descriptor = Assert.Single(descriptors);
|
||||||
|
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact(Skip = "#364")]
|
||||||
|
public void CreateDescriptor_AddsErrorForTagHelperWithDuplicateAttributeNames()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var errorSink = new ErrorSink();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var descriptors = TagHelperDescriptorFactory.CreateDescriptors(
|
||||||
|
AssemblyName,
|
||||||
|
typeof(DuplicateAttributeNameTagHelper),
|
||||||
|
errorSink);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(descriptors);
|
||||||
|
var error = Assert.Single(errorSink.Errors);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType()
|
public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType()
|
||||||
{
|
{
|
||||||
|
|
@ -867,7 +912,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
{
|
{
|
||||||
var errorFormat = "Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML " +
|
var errorFormat = "Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML " +
|
||||||
"attributes beginning with 'data-'.";
|
"attributes beginning with 'data-'.";
|
||||||
|
|
||||||
// type, expectedAttributeDescriptors, expectedErrors
|
// type, expectedAttributeDescriptors, expectedErrors
|
||||||
return new TheoryData<Type, IEnumerable<TagHelperAttributeDescriptor>, string[]>
|
return new TheoryData<Type, IEnumerable<TagHelperAttributeDescriptor>, string[]>
|
||||||
{
|
{
|
||||||
|
|
@ -1049,6 +1094,26 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DuplicateAttributeNameTagHelper
|
||||||
|
{
|
||||||
|
public string MyNameIsLegion { get; set; }
|
||||||
|
|
||||||
|
[HtmlAttributeName("my-name-is-legion")]
|
||||||
|
public string Fred { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NotBoundAttributeTagHelper
|
||||||
|
{
|
||||||
|
public string BoundProperty { get; set; }
|
||||||
|
|
||||||
|
[HtmlAttributeNotBound]
|
||||||
|
public string NotBoundProperty { get; set; }
|
||||||
|
|
||||||
|
[HtmlAttributeName("unused")]
|
||||||
|
[HtmlAttributeNotBound]
|
||||||
|
public string NamedNotBoundProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private class OverriddenAttributeTagHelper
|
private class OverriddenAttributeTagHelper
|
||||||
{
|
{
|
||||||
[HtmlAttributeName("SomethingElse")]
|
[HtmlAttributeName("SomethingElse")]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue