Render `maxlength` attribute for an input tag, when MaxLength or StringLength validation attributes are applied to the model class.
This commit is contained in:
parent
b7335ac768
commit
6d9aa281c5
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||||
|
|
@ -17,15 +18,18 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
public class MvcViewOptions : IEnumerable<ICompatibilitySwitch>
|
public class MvcViewOptions : IEnumerable<ICompatibilitySwitch>
|
||||||
{
|
{
|
||||||
private readonly CompatibilitySwitch<bool> _suppressTempDataAttributePrefix;
|
private readonly CompatibilitySwitch<bool> _suppressTempDataAttributePrefix;
|
||||||
|
private readonly CompatibilitySwitch<bool> _allowRenderingMaxLengthAttribute;
|
||||||
private readonly ICompatibilitySwitch[] _switches;
|
private readonly ICompatibilitySwitch[] _switches;
|
||||||
private HtmlHelperOptions _htmlHelperOptions = new HtmlHelperOptions();
|
private HtmlHelperOptions _htmlHelperOptions = new HtmlHelperOptions();
|
||||||
|
|
||||||
public MvcViewOptions()
|
public MvcViewOptions()
|
||||||
{
|
{
|
||||||
_suppressTempDataAttributePrefix = new CompatibilitySwitch<bool>(nameof(SuppressTempDataAttributePrefix));
|
_suppressTempDataAttributePrefix = new CompatibilitySwitch<bool>(nameof(SuppressTempDataAttributePrefix));
|
||||||
|
_allowRenderingMaxLengthAttribute = new CompatibilitySwitch<bool>(nameof(AllowRenderingMaxLengthAttribute));
|
||||||
_switches = new[]
|
_switches = new[]
|
||||||
{
|
{
|
||||||
_suppressTempDataAttributePrefix,
|
_suppressTempDataAttributePrefix,
|
||||||
|
_allowRenderingMaxLengthAttribute
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,6 +91,18 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
set => _suppressTempDataAttributePrefix.Value = value;
|
set => _suppressTempDataAttributePrefix.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that indicates whether the maxlength attribute should be rendered for compatible HTML elements,
|
||||||
|
/// when they're bound to models marked with either
|
||||||
|
/// <see cref="StringLengthAttribute"/> or <see cref="MaxLengthAttribute"/> attributes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If both attributes are specified, the one with the smaller value will be used for the rendered `maxlength` attribute.</remarks>
|
||||||
|
public bool AllowRenderingMaxLengthAttribute
|
||||||
|
{
|
||||||
|
get => _allowRenderingMaxLengthAttribute.Value;
|
||||||
|
set => _allowRenderingMaxLengthAttribute.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a list <see cref="IViewEngine"/>s used by this application.
|
/// Gets a list <see cref="IViewEngine"/>s used by this application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
@ -30,6 +31,10 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
private static readonly string[] _placeholderInputTypes =
|
private static readonly string[] _placeholderInputTypes =
|
||||||
new[] { "text", "search", "url", "tel", "email", "password", "number" };
|
new[] { "text", "search", "url", "tel", "email", "password", "number" };
|
||||||
|
|
||||||
|
// See: (http://www.w3.org/TR/html5/sec-forms.html#apply)
|
||||||
|
private static readonly string[] _maxLengthInputTypes =
|
||||||
|
new[] { "text", "search", "url", "tel", "email", "password" };
|
||||||
|
|
||||||
private readonly IAntiforgery _antiforgery;
|
private readonly IAntiforgery _antiforgery;
|
||||||
private readonly IModelMetadataProvider _metadataProvider;
|
private readonly IModelMetadataProvider _metadataProvider;
|
||||||
private readonly IUrlHelperFactory _urlHelperFactory;
|
private readonly IUrlHelperFactory _urlHelperFactory;
|
||||||
|
|
@ -92,8 +97,18 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
|
|
||||||
// Underscores are fine characters in id's.
|
// Underscores are fine characters in id's.
|
||||||
IdAttributeDotReplacement = optionsAccessor.Value.HtmlHelperOptions.IdAttributeDotReplacement;
|
IdAttributeDotReplacement = optionsAccessor.Value.HtmlHelperOptions.IdAttributeDotReplacement;
|
||||||
|
|
||||||
|
AllowRenderingMaxLengthAttribute = optionsAccessor.Value.AllowRenderingMaxLengthAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value that indicates whether the maxlength attribute should be rendered for compatible HTML input elements,
|
||||||
|
/// when they're bound to models marked with either
|
||||||
|
/// <see cref="StringLengthAttribute"/> or <see cref="MaxLengthAttribute"/> attributes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If both attributes are specified, the one with the smaller value will be used for the rendered `maxlength` attribute.</remarks>
|
||||||
|
protected bool AllowRenderingMaxLengthAttribute { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string IdAttributeDotReplacement { get; }
|
public string IdAttributeDotReplacement { get; }
|
||||||
|
|
||||||
|
|
@ -724,6 +739,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
||||||
|
if (AllowRenderingMaxLengthAttribute)
|
||||||
|
{
|
||||||
|
AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
||||||
|
}
|
||||||
|
|
||||||
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||||
|
|
||||||
// If there are any errors for a named field, we add this CSS attribute.
|
// If there are any errors for a named field, we add this CSS attribute.
|
||||||
|
|
@ -1239,6 +1259,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AllowRenderingMaxLengthAttribute && _maxLengthInputTypes.Contains(suppliedTypeString))
|
||||||
|
{
|
||||||
|
AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
|
||||||
|
}
|
||||||
|
|
||||||
var valueParameter = FormatValue(value, format);
|
var valueParameter = FormatValue(value, format);
|
||||||
var usedModelState = false;
|
var usedModelState = false;
|
||||||
switch (inputType)
|
switch (inputType)
|
||||||
|
|
@ -1373,6 +1398,43 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a maxlength attribute to the <paramref name="tagBuilder" />.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="viewData">A <see cref="ViewDataDictionary"/> instance for the current scope.</param>
|
||||||
|
/// <param name="tagBuilder">A <see cref="TagBuilder"/> instance.</param>
|
||||||
|
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the <paramref name="expression"/>.</param>
|
||||||
|
/// <param name="expression">Expression name, relative to the current model.</param>
|
||||||
|
protected virtual void AddMaxLengthAttribute(
|
||||||
|
ViewDataDictionary viewData,
|
||||||
|
TagBuilder tagBuilder,
|
||||||
|
ModelExplorer modelExplorer,
|
||||||
|
string expression)
|
||||||
|
{
|
||||||
|
modelExplorer = modelExplorer ?? ExpressionMetadataProvider.FromStringExpression(
|
||||||
|
expression,
|
||||||
|
viewData,
|
||||||
|
_metadataProvider);
|
||||||
|
|
||||||
|
int? maxLengthValue = null;
|
||||||
|
foreach (var attribute in modelExplorer.Metadata.ValidatorMetadata)
|
||||||
|
{
|
||||||
|
if (attribute is MaxLengthAttribute maxLengthAttribute && (!maxLengthValue.HasValue || maxLengthValue.Value > maxLengthAttribute.Length))
|
||||||
|
{
|
||||||
|
maxLengthValue = maxLengthAttribute.Length;
|
||||||
|
}
|
||||||
|
else if (attribute is StringLengthAttribute stringLengthAttribute && (!maxLengthValue.HasValue || maxLengthValue.Value > stringLengthAttribute.MaximumLength))
|
||||||
|
{
|
||||||
|
maxLengthValue = stringLengthAttribute.MaximumLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxLengthValue.HasValue)
|
||||||
|
{
|
||||||
|
tagBuilder.MergeAttribute("maxlength", maxLengthValue.Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds validation attributes to the <paramref name="tagBuilder" /> if client validation
|
/// Adds validation attributes to the <paramref name="tagBuilder" /> if client validation
|
||||||
/// is enabled.
|
/// is enabled.
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
values[nameof(MvcViewOptions.SuppressTempDataAttributePrefix)] = true;
|
values[nameof(MvcViewOptions.SuppressTempDataAttributePrefix)] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Version >= CompatibilityVersion.Version_2_2)
|
||||||
|
{
|
||||||
|
values[nameof(MvcViewOptions.AllowRenderingMaxLengthAttribute)] = true;
|
||||||
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
|
|
@ -190,6 +191,169 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
Assert.Equal(expected, attribute.Value);
|
Assert.Equal(expected, attribute.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithMaxLength), ModelWithMaxLengthMetadata.MaxLengthAttributeValue)]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithStringLength), ModelWithMaxLengthMetadata.StringLengthAttributeValue)]
|
||||||
|
public void GenerateTextArea_RendersMaxLength(string expression, int expectedValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), expression);
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateTextArea(viewContext, modelExplorer, expression, rows: 1, columns: 1, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
Assert.Equal(expectedValue, Int32.Parse(attribute.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithMaxLength), ModelWithMaxLengthMetadata.MaxLengthAttributeValue)]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithStringLength), ModelWithMaxLengthMetadata.StringLengthAttributeValue)]
|
||||||
|
public void GeneratePassword_RendersMaxLength(string expression, int expectedValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), expression);
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GeneratePassword(viewContext, modelExplorer, expression, null, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
Assert.Equal(expectedValue, Int32.Parse(attribute.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithMaxLength), ModelWithMaxLengthMetadata.MaxLengthAttributeValue)]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithStringLength), ModelWithMaxLengthMetadata.StringLengthAttributeValue)]
|
||||||
|
public void GenerateTextBox_RendersMaxLength(string expression, int expectedValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), expression);
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateTextBox(viewContext, modelExplorer, expression, null, null, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
Assert.Equal(expectedValue, Int32.Parse(attribute.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateTextBox_RendersMaxLength_WithMinimumValueFromBothAttributes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), nameof(ModelWithMaxLengthMetadata.FieldWithBothAttributes));
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateTextBox(viewContext, modelExplorer, nameof(ModelWithMaxLengthMetadata.FieldWithBothAttributes), null, null, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
Assert.Equal(Math.Min(ModelWithMaxLengthMetadata.MaxLengthAttributeValue, ModelWithMaxLengthMetadata.StringLengthAttributeValue), Int32.Parse(attribute.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateTextBox_DoesNotRenderMaxLength_WhenNoAttributesPresent()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), nameof(ModelWithMaxLengthMetadata.FieldWithoutAttributes));
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateTextBox(viewContext, modelExplorer, nameof(ModelWithMaxLengthMetadata.FieldWithoutAttributes), null, null, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotContain(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithMaxLength), ModelWithMaxLengthMetadata.MaxLengthAttributeValue)]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithStringLength), ModelWithMaxLengthMetadata.StringLengthAttributeValue)]
|
||||||
|
public void GenerateTextBox_SearchType_RendersMaxLength(string expression, int expectedValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), expression);
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
{ "type", "search"}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateTextBox(viewContext, modelExplorer, expression, null, null, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
Assert.Equal(expectedValue, Int32.Parse(attribute.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithMaxLength))]
|
||||||
|
[InlineData(nameof(ModelWithMaxLengthMetadata.FieldWithStringLength))]
|
||||||
|
public void GenerateHidden_DoesNotRenderMaxLength(string expression)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
var htmlGenerator = GetGenerator(metadataProvider);
|
||||||
|
var viewContext = GetViewContext<ModelWithMaxLengthMetadata>(model: null, metadataProvider: metadataProvider);
|
||||||
|
var modelMetadata = metadataProvider.GetMetadataForProperty(typeof(ModelWithMaxLengthMetadata), expression);
|
||||||
|
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, null);
|
||||||
|
var htmlAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "name", "testElement" },
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var tagBuilder = htmlGenerator.GenerateHidden(viewContext, modelExplorer, expression, null, false, htmlAttributes);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.DoesNotContain(tagBuilder.Attributes, a => a.Key == "maxlength");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GenerateValidationMessage_WithNullExpression_Throws()
|
public void GenerateValidationMessage_WithNullExpression_Throws()
|
||||||
{
|
{
|
||||||
|
|
@ -759,7 +923,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
|
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
|
||||||
{
|
{
|
||||||
var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>();
|
||||||
mvcViewOptionsAccessor.SetupGet(accessor => accessor.Value).Returns(new MvcViewOptions());
|
mvcViewOptionsAccessor.SetupGet(accessor => accessor.Value).Returns(new MvcViewOptions() { AllowRenderingMaxLengthAttribute = true });
|
||||||
|
|
||||||
var htmlEncoder = Mock.Of<HtmlEncoder>();
|
var htmlEncoder = Mock.Of<HtmlEncoder>();
|
||||||
var antiforgery = new Mock<IAntiforgery>();
|
var antiforgery = new Mock<IAntiforgery>();
|
||||||
|
|
@ -820,6 +984,24 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
All = -1,
|
All = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ModelWithMaxLengthMetadata
|
||||||
|
{
|
||||||
|
internal const int MaxLengthAttributeValue = 77;
|
||||||
|
internal const int StringLengthAttributeValue = 7;
|
||||||
|
|
||||||
|
[MaxLength(MaxLengthAttributeValue)]
|
||||||
|
public string FieldWithMaxLength { get; set; }
|
||||||
|
|
||||||
|
[StringLength(StringLengthAttributeValue)]
|
||||||
|
public string FieldWithStringLength { get; set; }
|
||||||
|
|
||||||
|
public string FieldWithoutAttributes { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(MaxLengthAttributeValue)]
|
||||||
|
[StringLength(StringLengthAttributeValue)]
|
||||||
|
public string FieldWithBothAttributes { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
private class Model
|
private class Model
|
||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue