| File: DataAnnotationsClientModelValidatorProvider.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.DataAnnotations\src\Microsoft.AspNetCore.Mvc.DataAnnotations.csproj (Microsoft.AspNetCore.Mvc.DataAnnotations) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.Extensions.Localization; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Mvc.DataAnnotations; /// <summary> /// An implementation of <see cref="IClientModelValidatorProvider"/> which provides client validators /// for attributes which derive from <see cref="ValidationAttribute"/>. It also provides /// a validator for types which implement <see cref="IClientModelValidator"/>. /// The logic to support <see cref="IClientModelValidator"/> /// is implemented in <see cref="ValidationAttributeAdapter{TAttribute}"/>. /// </summary> internal sealed class DataAnnotationsClientModelValidatorProvider : IClientModelValidatorProvider { private readonly IOptions<MvcDataAnnotationsLocalizationOptions> _options; private readonly IStringLocalizerFactory? _stringLocalizerFactory; private readonly IValidationAttributeAdapterProvider _validationAttributeAdapterProvider; /// <summary> /// Create a new instance of <see cref="DataAnnotationsClientModelValidatorProvider"/>. /// </summary> /// <param name="validationAttributeAdapterProvider">The <see cref="IValidationAttributeAdapterProvider"/> /// that supplies <see cref="IAttributeAdapter"/>s.</param> /// <param name="options">The <see cref="IOptions{MvcDataAnnotationsLocalizationOptions}"/>.</param> /// <param name="stringLocalizerFactory">The <see cref="IStringLocalizerFactory"/>.</param> public DataAnnotationsClientModelValidatorProvider( IValidationAttributeAdapterProvider validationAttributeAdapterProvider, IOptions<MvcDataAnnotationsLocalizationOptions> options, IStringLocalizerFactory? stringLocalizerFactory) { ArgumentNullException.ThrowIfNull(validationAttributeAdapterProvider); ArgumentNullException.ThrowIfNull(options); _validationAttributeAdapterProvider = validationAttributeAdapterProvider; _options = options; _stringLocalizerFactory = stringLocalizerFactory; } /// <inheritdoc /> public void CreateValidators(ClientValidatorProviderContext context) { ArgumentNullException.ThrowIfNull(context); IStringLocalizer? stringLocalizer = null; if (_options.Value.DataAnnotationLocalizerProvider != null && _stringLocalizerFactory != null) { // This will pass first non-null type (either containerType or modelType) to delegate. // Pass the root model type(container type) if it is non null, else pass the model type. stringLocalizer = _options.Value.DataAnnotationLocalizerProvider( context.ModelMetadata.ContainerType ?? context.ModelMetadata.ModelType, _stringLocalizerFactory); } var hasRequiredAttribute = false; var results = context.Results; // Read interface .Count once rather than per iteration var resultsCount = results.Count; for (var i = 0; i < resultsCount; i++) { var validatorItem = results[i]; if (validatorItem.Validator != null) { // Check if a required attribute is already cached. hasRequiredAttribute |= validatorItem.Validator is RequiredAttributeAdapter; continue; } var attribute = validatorItem.ValidatorMetadata as ValidationAttribute; if (attribute == null) { continue; } hasRequiredAttribute |= attribute is RequiredAttribute; var adapter = _validationAttributeAdapterProvider.GetAttributeAdapter(attribute, stringLocalizer); if (adapter != null) { validatorItem.Validator = adapter; validatorItem.IsReusable = true; } } if (!hasRequiredAttribute && context.ModelMetadata.IsRequired) { // Add a default '[Required]' validator for generating HTML if necessary. context.Results.Add(new ClientValidatorItem { Validator = _validationAttributeAdapterProvider.GetAttributeAdapter( new RequiredAttribute(), stringLocalizer), IsReusable = true }); } } }