예제 #1
0
	/// <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");
		}
	}
예제 #2
0
        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);
        }
예제 #3
0
        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);
                }
            }
        }
예제 #4
0
            private readonly object        _token; // Explicitly using ReferenceEquality for tokens.

            public Key(ModelMetadata metadata, object token)
            {
                _metadata = metadata;
                _token    = token;
            }
예제 #5
0
        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);
            }
        }
예제 #6
0
        /// <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);
예제 #10
0
 /// <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);