Подтвердить что ты не робот

Как предоставить предупреждения во время проверки в ASP.NET MVC?

Иногда пользовательский ввод не является строго недействительным, но может считаться проблематичным.

Например:

  • Пользователь вводит длинное предложение в однострочном поле Name. Он, вероятно, должен вместо этого использовали поле Description.
  • Пользователь вводит Name, который очень похож на объект существующего объекта. Возможно, он ввел один и тот же объект, но не понимал, что он уже существует, или какой-то пользователь одновременно вошел в него.

Некоторые из них могут быть легко проверены на стороне клиента, некоторые требуют проверки на стороне сервера.

Какой лучший способ, возможно, что-то похожее на проверку DataAnnotations, для предоставления предупреждений пользователю в таких случаях? Ключевым моментом здесь является то, что пользователь должен быть в состоянии переопределить предупреждение и по-прежнему отправлять форму (или повторно отправить форму, в зависимости от реализации).

Наиболее жизнеспособным решением, которое приходит на ум, является создание некоторого атрибута, аналогичного CustomValidationAttribute, который может сделать вызов AJAX и отображать некоторый текст предупреждения, но не влияет на ModelState. Предполагаемое использование:

[WarningOnFieldLength(MaxLength = 150)]
[WarningOnPossibleDuplicate()]
public string Name { get; set; }

В представлении:

@Html.EditorFor(model => model.Name)
@Html.WarningMessageFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)

Итак, любые идеи?

4b9b3361

Ответ 1

Общий дизайн

Для начала, я считаю, что вам нужно будет как-то отслеживать, если пользователь решит игнорировать предупреждения. Простой и прозрачный способ сделать это - установить флажок Ignore Warnings, который пользователь должен будет проверить перед отправкой. Другим вариантом является то, что они должны отправить форму два раза и игнорировать предупреждения на второй submit; то вам, вероятно, понадобится скрытое поле IgnoreWarnings. Могут быть другие конструкции, но для простоты я пошлю первый вариант.

Короче говоря, подход заключается в создании

  • Пользовательский атрибут аннотации данных для всех моделей просмотров, поддерживающих тип проверки предупреждения;
  • Известный базовый класс, который наследует модели вида:
  • Нам придется дублировать логику JavaScript для каждого настраиваемого атрибута.

Обратите внимание, что приведенный ниже код просто иллюстрирует подход, и я должен принять множество вещей, не зная полного контекста.

Показать модель

В этом сценарии лучше всего отделить модель представления от реальной модели, которая в любом случае является хорошей идеей. Один из возможных подходов состоит в том, чтобы иметь базовый класс для всех моделей представлений, которые поддерживают предупреждения:

public abstract class BaseViewModel
{
    public bool IgnoreWarnings { get; set; }
}

Основная причина, по которой модель должна быть отдельной, заключается в том, что нет никакого смысла хранить свойство IgnoreWarnings в вашей базе данных.

Ваша производная модель просмотра будет выглядеть следующим образом:

public class YourViewModel : BaseViewModel
{
    [Required]
    [StringLengthWarning(MaximumLength = 5, ErrorMessage = "Your Warning Message")]
    public string YourProperty { get; set; }
}

StringLengthWarning - это специальный атрибут аннотации данных для проверки на стороне сервера и на стороне клиента. Он просто поддерживает максимальную длину и может быть легко расширен с любыми другими необходимыми свойствами.

Атрибут аннотации данных

Ядром атрибута является IsValid(value, validationContext метод.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public class StringLengthWarningAttribute : ValidationAttribute, IClientValidatable 
{
    public int MaximumLength { get; set; }

    public override bool IsValid(object value)
    {
        return true;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var model = validationContext.ObjectInstance as BaseViewModel;
        var str = value as string;
        if (!model.IgnoreWarnings && (string.IsNullOrWhiteSpace(str) || str.Length > MaximumLength))
            return new ValidationResult(ErrorMessage);
        return base.IsValid(value, validationContext);
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        yield return new StringLengthWarningValidationRule(MaximumLength, ErrorMessage);
    }
}

Атрибут реализует IClientValidatable и использует правило проверки клиента:

public class StringLengthWarningValidationRule : ModelClientValidationRule
{
    public StringLengthWarningValidationRule(int maximumLength, string errorMessage)
    {
        ErrorMessage = errorMessage;
        ValidationType = "stringlengthwarning";
        ValidationParameters.Add("maximumlength", maximumLength);
        ValidationParameters.Add("ignorewarningsfield", "IgnoreWarnings");
    }
}

Клиентский JavaScript

Наконец, чтобы заставить его работать, вам понадобится следующий JavaScript, указанный в вашем представлении:

$(function () {
    $.validator.addMethod('stringlengthwarning', function (value, element, params) {
        var maximumlength = params['maximumlength'];
        var ignorewarningsfield = params['ignorewarningsfield'];

        var ctl = $("#" + ignorewarningsfield);
        if (ctl == null || ctl.is(':checked'))
            return true;
        return value.length <= maximumlength;
    });

    $.validator.unobtrusive.adapters.add("stringlengthwarning", ["maximumlength", "ignorewarningsfield"], function (options) {
        var value = {
            maximumlength: options.params.maximumlength,
            ignorewarningsfield: options.params.ignorewarningsfield
        };
        options.rules["stringlengthwarning"] = value;
        if (options.message) {
            options.messages["stringlengthwarning"] = options.message;
        }
    });

}(jQuery));

JavaScript делает некоторые предположения, которые вы, возможно, захотите пересмотреть (имя check-box и т.д.).

ОБНОВЛЕНИЕ: HTML-помощники

Чтобы отображать сообщения проверки отдельно для ошибок и предупреждений, потребуется несколько помощников. Следующий класс предоставляет образец:

public static class  MessageHelpers
{
    public static MvcHtmlString WarningMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] != null)
            return htmlHelper.ValidationMessageFor(expression);
        return MvcHtmlString.Empty;
    }

    public static MvcHtmlString ErrorMessageFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
    {
        if (htmlHelper.ViewData.ModelState["IgnoreWarnings"] == null)
            return htmlHelper.ValidationMessageFor(expression);
        return MvcHtmlString.Empty;
    }
}

В представлении они могут использоваться как обычно:

        @Html.EditorFor(model => model.YourProperty)
        @Html.ErrorMessageFor(model => model.YourProperty)
        @Html.WarningMessageFor(model => model.YourProperty)

Ответ 2

Просто комментируйте возможную повторную реализацию, которую вы упомянули...

Для "вы хотели это сделать?" тип проверки, с точки зрения пользователя, необходимость повторной отправки формы, основанной на предположении, что они допустили ошибку, может быть очень раздражающей. Я бы выполнил эту "псевдо-валидацию" на стороне клиента с помощью javascript и (надеюсь, быстро) ajax-вызовов, если вам нужно нажать на сервер.

Я также попытаюсь отобразить предупреждения на событиях размытия ввода/изменения, чтобы они отображались до того, как пользователь набрал submit. Возможно, это не практично во всех ситуациях, но я просто подумал, что выброшу его там.

Ответ 3

Вы можете использовать функцию depends проверки jquery, чтобы упростить вашу жизнь.

Исх.

@Html.LabelFor(m => m.UserName)
@Html.TextBoxFor(m => m.UserName)
@Html.ValidationMessageFor(m => m.UserName)

<label>Ignore Warnings</label>
<input id="ignore-warnings" type="checkbox" />

<script>
  $(function () {
    $("#UserName").rules("add", {
      minlength: {
        param: 6,
        depends: function (element) {
          return !$("#ignore-warnings").attr('checked');
        }
      },

      // server side remote validation for duplicate check
      remote: {
        param: '/account/duplicate',
        depends: function (element) {
          return !$("#ignore-warnings").attr('checked');
        }
      }
    });
  });
</script>

Ответ 4

Это всего лишь эскиз возможного решения. Существует множество примеров добавления пользовательских атрибутов (включая выше), поэтому я пропущу этот бит.

Возможно, можно добавить использование игнорирования в функцию проверки jQuery.

Затем используйте

$("form").validate({  
ignore: ".warning-only"
});

и использовать валидатор на стороне клиента, чтобы добавить класс "только для предупреждения" после первого прохода через валидатор. Затем это должно разрешить отправку формы на сервер.

Как я уже сказал, просто набросок, но это то, что я изучал для использования.

Ответ 5

Вот способ сделать предупреждение без написания кода на стороне сервера. Добавьте класс "ignore-validation" к желаемым недействительным элементам в представлении формы, а в вашем настраиваемом методе проверки верните "true", если элемент имеет этот класс (если у него есть класс, это означает, что форма была отправлена ​​один раз). Вам также необходимо удалить класс "игнорировать-валидацию" из #IdOfInput при размытии или изменении, в зависимости от вида управления, который этот бит кода здесь не представлен:

<script type="text/javascript">    
$.validator.addMethod('isValidCustomMethod', function (value, element) {
        if($(element).hasClass('ignore-validation')){
            return true;
        }
        var isValid = false;    //your code to do validation would actually go here
        return isValid;
    });

$(document).ready(function () {

    $('#IdOfInput').rules('add', { isValidCustomMethod: true, messages: { isValidCustomMethod: 'Your warning message here'} });

    $('form').submit(function () {
                    $(this).validate().invalidElements().each(function () {
                        if($(this).attr('id')=='IdOfInput'){
                            $(this).addClass('ignore-validation');
                        }
                    });
                });
        }); 
    </script>