/// <inheritdoc /> /// <remarks>Does nothing if <see cref="TitleFor"/> is <c>null</c>.</remarks> public override void Process(TagHelperContext context, TagHelperOutput output) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (output == null) { throw new ArgumentNullException(nameof(output)); } Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata metadata = TitleFor.Metadata; if (metadata == null) { throw new InvalidOperationException(string.Format("No provided metadata ({0})", ForAttributeName)); } if (!string.IsNullOrWhiteSpace(metadata.Description)) { output.Attributes.SetAttribute("title", metadata.Description); output.Attributes.SetAttribute("data-toggle", "tooltip"); output.Attributes.SetAttribute("data-placement", "bottom"); } }
private static Type GetNonNullableModelType(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata modelMetadata) { Type realModelType = modelMetadata.ModelType; Type underlyingType = Nullable.GetUnderlyingType(realModelType); if (underlyingType != null) { realModelType = underlyingType; } return(realModelType); }
public ModelMetadata( Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata metadata, ModelMetadataOptions options ) { m = metadata; List <string> manual = new List <string>() { nameof(Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.Properties), nameof(DefaultModelMetadata.Attributes) }; foreach (var prop in this.GetType().GetProperties()) { if (manual.Contains(prop.Name)) { continue; } var sourceValue = m.GetType().GetProperty(prop.Name).GetValue(m); prop.SetValue(this, sourceValue); } if (options.Models.Values.Any(t => t == metadata.ModelType)) { this.Properties = new List <ModelMetadata>(); foreach (var p in m.Properties) { this.Properties.Add(new ModelMetadata(p, options)); } } if (metadata is DefaultModelMetadata defMeta) { this.Attributes = new List <object>(); foreach (var attr in defMeta.Attributes.Attributes) { this.Attributes.Add(attr); } } }
private readonly object _token; // Explicitly using ReferenceEquality for tokens. public Key(ModelMetadata metadata, object token) { _metadata = metadata; _token = token; }
public static void ClearValidationStateForModel( ModelMetadata modelMetadata, ModelStateDictionary modelState, string?modelKey) { if (modelMetadata == null) { throw new ArgumentNullException(nameof(modelMetadata)); } if (modelState == null) { throw new ArgumentNullException(nameof(modelState)); } if (string.IsNullOrEmpty(modelKey)) { // If model key is empty, we have to do a best guess to try and clear the appropriate // keys. Clearing the empty prefix would clear the state of ALL entries, which might wipe out // data from other models. if (modelMetadata.IsEnumerableType) { // We expect that any key beginning with '[' is an index. We can't just infer the indexes // used, so we clear all keys that look like <empty prefix -> index>. // // In the unlikely case that multiple top-level collections where bound to the empty prefix, // you're just out of luck. foreach (var kvp in modelState) { if (kvp.Key.Length > 0 && kvp.Key[0] == '[') { // Starts with an indexer kvp.Value.Errors.Clear(); kvp.Value.ValidationState = ModelValidationState.Unvalidated; } } } else if (modelMetadata.IsComplexType) { for (var i = 0; i < modelMetadata.Properties.Count; i++) { var property = modelMetadata.Properties[i]; modelState.ClearValidationState((property.BinderModelName ?? property.PropertyName) !); } } else { // Simple types bind to a single entry. So clear the entry with the empty-key, in the // unlikely event that it has errors. var entry = modelState[string.Empty]; if (entry != null) { entry.Errors.Clear(); entry.ValidationState = ModelValidationState.Unvalidated; } } } else { // If model key is non-empty, we just want to clear all keys with that prefix. We expect // model binding to have only used this key (and suffixes) for all entries related to // this model. modelState.ClearValidationState(modelKey); } }
/// <summary> /// Binds a model specified by <paramref name="parameter"/> using <paramref name="value"/> as the initial value. /// </summary> /// <param name="actionContext">The <see cref="ActionContext"/>.</param> /// <param name="modelBinder">The <see cref="IModelBinder"/>.</param> /// <param name="valueProvider">The <see cref="IValueProvider"/>.</param> /// <param name="parameter">The <see cref="ParameterDescriptor"/></param> /// <param name="metadata">The <see cref="ModelMetadata"/>.</param> /// <param name="value">The initial model value.</param> /// <returns>The result of model binding.</returns> public virtual async Task <ModelBindingResult> BindModelAsync( ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, object value) { if (actionContext == null) { throw new ArgumentNullException(nameof(actionContext)); } if (modelBinder == null) { throw new ArgumentNullException(nameof(modelBinder)); } if (valueProvider == null) { throw new ArgumentNullException(nameof(valueProvider)); } if (parameter == null) { throw new ArgumentNullException(nameof(parameter)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } Logger.AttemptingToBindParameterOrProperty(parameter, metadata); if (parameter.BindingInfo?.RequestPredicate?.Invoke(actionContext) == false) { Logger.ParameterBinderRequestPredicateShortCircuit(parameter, metadata); return(ModelBindingResult.Failed()); } var modelBindingContext = DefaultModelBindingContext.CreateBindingContext( actionContext, valueProvider, metadata, parameter.BindingInfo, parameter.Name); modelBindingContext.Model = value; var parameterModelName = parameter.BindingInfo?.BinderModelName ?? metadata.BinderModelName; if (parameterModelName != null) { // The name was set explicitly, always use that as the prefix. modelBindingContext.ModelName = parameterModelName; } else if (modelBindingContext.ValueProvider.ContainsPrefix(parameter.Name)) { // We have a match for the parameter name, use that as that prefix. modelBindingContext.ModelName = parameter.Name; } else { // No match, fallback to empty string as the prefix. modelBindingContext.ModelName = string.Empty; } await modelBinder.BindModelAsync(modelBindingContext); Logger.DoneAttemptingToBindParameterOrProperty(parameter, metadata); var modelBindingResult = modelBindingContext.Result; if (_objectModelValidator is ObjectModelValidator baseObjectValidator) { Logger.AttemptingToValidateParameterOrProperty(parameter, metadata); EnforceBindRequiredAndValidate( baseObjectValidator, actionContext, parameter, metadata, modelBindingContext, modelBindingResult); Logger.DoneAttemptingToValidateParameterOrProperty(parameter, metadata); } else { // For legacy implementations (which directly implemented IObjectModelValidator), fall back to the // back-compatibility logic. In this scenario, top-level validation attributes will be ignored like // they were historically. if (modelBindingResult.IsModelSet) { _objectModelValidator.Validate( actionContext, modelBindingContext.ValidationState, modelBindingContext.ModelName, modelBindingResult.Model); } } return(modelBindingResult); }
/// <summary> /// Attempts to add the specified <paramref name="exception"/> to the <see cref="ModelStateEntry.Errors"/> /// instance that is associated with the specified <paramref name="key"/>. If the maximum number of allowed /// errors has already been recorded, ensures that a <see cref="TooManyModelErrorsException"/> exception is /// recorded instead. /// </summary> /// <param name="key">The key of the <see cref="ModelStateEntry"/> to add errors to.</param> /// <param name="exception">The <see cref="Exception"/> to add. Some exception types will be replaced with /// a descriptive error message.</param> /// <param name="metadata">The <see cref="ModelMetadata"/> associated with the model.</param> /// <returns> /// <c>True</c> if the given error was added, <c>false</c> if the error was ignored. /// See <see cref="MaxAllowedErrors"/>. /// </returns> public bool TryAddModelError(string key, Exception exception, ModelMetadata metadata) { if (key == null) { throw new ArgumentNullException(nameof(key)); } if (exception == null) { throw new ArgumentNullException(nameof(exception)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (ErrorCount >= MaxAllowedErrors - 1) { EnsureMaxErrorsReachedRecorded(); return(false); } if (exception is FormatException || exception is OverflowException) { // Convert FormatExceptions and OverflowExceptions to Invalid value messages. TryGetValue(key, out var entry); // Not using metadata.GetDisplayName() or a single resource to avoid strange messages like // "The value '' is not valid." (when no value was provided, not even an empty string) and // "The supplied value is invalid for Int32." (when error is for an element or parameter). var messageProvider = metadata.ModelBindingMessageProvider; var name = metadata.DisplayName ?? metadata.PropertyName; string errorMessage; if (entry == null && name == null) { errorMessage = messageProvider.NonPropertyUnknownValueIsInvalidAccessor(); } else if (entry == null) { errorMessage = messageProvider.UnknownValueIsInvalidAccessor(name); } else if (name == null) { errorMessage = messageProvider.NonPropertyAttemptedValueIsInvalidAccessor(entry.AttemptedValue); } else { errorMessage = messageProvider.AttemptedValueIsInvalidAccessor(entry.AttemptedValue, name); } return(TryAddModelError(key, errorMessage)); } else if ((exception is InputFormatterException || exception is ValueProviderException) && !string.IsNullOrEmpty(exception.Message)) { // InputFormatterException, ValueProviderException is a signal that the message is safe to expose to clients return(TryAddModelError(key, exception.Message)); } ErrorCount++; AddModelErrorCore(key, exception); return(true); }
/// <summary> /// Creates an <see cref="IModelBinder"/> for the given <paramref name="metadata"/> /// and <paramref name="bindingInfo"/>. /// </summary> /// <param name="metadata">The <see cref="ModelMetadata"/> for the model.</param> /// <param name="bindingInfo">The <see cref="BindingInfo"/> that should be used /// for creating the binder.</param> /// <returns>An <see cref="IModelBinder"/>.</returns> public virtual IModelBinder CreateBinder(ModelMetadata metadata, BindingInfo bindingInfo) { throw new NotSupportedException(); }
/// <summary> /// Creates an <see cref="IModelBinder"/> for the given <paramref name="metadata"/>. /// </summary> /// <param name="metadata">The <see cref="ModelMetadata"/> for the model.</param> /// <returns>An <see cref="IModelBinder"/>.</returns> public abstract IModelBinder CreateBinder(ModelMetadata metadata);
/// <summary> /// Pushes a layer of state onto this context. <see cref="IModelBinder"/> implementations will call this as /// part of recursion when binding properties or collection items. /// </summary> /// <param name="modelMetadata"> /// <see cref="ModelBinding.ModelMetadata"/> to assign to the <see cref="ModelMetadata"/> property. /// </param> /// <param name="fieldName">Name to assign to the <see cref="FieldName"/> property.</param> /// <param name="modelName">Name to assign to the <see cref="ModelName"/> property.</param> /// <param name="model">Instance to assign to the <see cref="Model"/> property.</param> /// <returns> /// A <see cref="NestedScope"/> scope object which should be used in a <c>using</c> statement where /// <see cref="EnterNestedScope(ModelMetadata, string, string, object)"/> is called. /// </returns> public abstract NestedScope EnterNestedScope( ModelMetadata modelMetadata, string fieldName, string modelName, object model);