Как я могу вернуть дополнительную информацию (т. е. больше, чем просто поле => сообщение об ошибке) из пользовательского кода проверки в контроллер или представление?

Я ищу способ вернуть следующую информацию из моего пользовательского кода проверки:

public enum ValidationErrorTypeFlags
{
    Error_Input = 1 << 0,               // a "field specific" error which is checked both clientside and serverside 
    Error_Input_ServerSide = 1 << 1,    // a "field specific" error which can only be checked serverside
    Error_General = 1 << 2              // serverside general error
}

Внутри кода проверки (либо IValidatableObject, либо ValidationAttribute), когда я обнаруживаю ошибку, я хотел бы иметь возможность связать один из вышеуказанных типов ошибок с ValidationResult.

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

В настоящее время я использую MVC 3 (с удовольствием обновился до 4).

NB:

  • ModelState не сохраняет ValidationResults AFAIK — вы можете получить доступ только к ошибкам в ViewData.ModelState.Values.Items[x].Errors — и они были преобразованы в System.Web.Mvc.ModelError
  • Кажется, что проверка MVC позволяет вам получить доступ к результатам проверки типа [ключ, «сообщение об ошибке»] только после завершения проверки.

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

var field = new[] { validationContext.DisplayName };
return new ValidationResult("+Invalid format - use yyyy-mm-dd", field);

А затем ищите сообщения об ошибках, которые начинаются с +,-,* в контроллере.


person Ilan    schedule 15.06.2012    source источник
comment
Я насчитал довольно много разных мест в MVC3, где ModelValidationError преобразуется в ModelState, поэтому я не думаю, что есть простой способ сделать это.   -  person IanBru    schedule 24.10.2012


Ответы (1)


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

public class CustomValidationResult: ValidationResult
{
   // additional properties
}

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

Обновление:

Вышеупомянутая идея не работает, потому что класс ValidationResult находится в сборке DataAnnotations, и они преобразуются в ModelValidationResult, и это все, что мы можем получить в MVC.

Кажется, что передача дополнительной информации из проверки аннотаций данных в MVC выглядит не совсем просто!

Я просмотрел исходный код и обнаружил, что именно ValidatableObjectAdapter преобразует IEnumerable<ValidationResult> в IEnumerable<ModelValidationResult>. Я не вижу особых преимуществ в расширении этого класса, но мы можем легко создать собственный ValidatableObjectAdapter, реализовав ModelValidator и продублировав код Validate.

Мы должны создать пользовательский ModelValidationResult и пользовательский ValidationResult (именно этот пользовательский ValidationResult мы будем возвращать из проверок), и в метод ConvertResults мы можем поместить наш код преобразования, который заботится о дополнительной информации.

public class CustomValidatableObjectAdapter : ModelValidator
{
    public CustomValidatableObjectAdapter(ModelMetadata metadata, ControllerContext context)
      : base(metadata, context)
    {
    }

    public override IEnumerable<ModelValidationResult> Validate(object container)
    {
      object model = Metadata.Model;
      if (model == null)
      {
        return Enumerable.Empty<ModelValidationResult>();
      }

      IValidatableObject validatable = model as IValidatableObject;
      if (validatable == null)
      {
        throw new Exception("model is of not type validatable");
      }

      ValidationContext validationContext = new ValidationContext(validatable, null, null);

      return ConvertResults(validatable.Validate(validationContext));
    }

    private IEnumerable<ModelValidationResult> ConvertResults(IEnumerable<ValidationResult> results)
    {
      foreach (ValidationResult result in results)
      {
        // iterate the ValidationResult enumeration and cast each into CustomValidationResult
        // and conver them into enumeration of CustomModelValidationResult.
      }
    }
}

Наконец, мы должны сказать DataAnnotationsModelValidatorProvider использовать это наше CustomValidatableObjectAdapter в событии Application_Start Global.asax.cs.

DataAnnotationsModelValidatorProvider.RegisterDefaultValidatableObjectAdapterFactory((metadata, context) => new CustomValidatableObjectAdapter(metadata, context));

Итак, вам нужно создать пользовательский ValidationResult, пользовательский ModelValidationResult и пользовательский ValidatableObjectAdapter.

Я не проверял это, но я надеюсь, что это сработает. Я могу предложить лучшее и более простое решение, чем это.

person VJAI    schedule 16.06.2012
comment
Спасибо за ответ. Я пробовал это раньше, но не смог найти переменную, доступную в контроллере, которую я мог бы использовать. Не могли бы вы указать мне правильное направление? Я пробовал ModelState, но, как уже упоминалось, у него есть только ModelError, а не ValidationResults. - person Ilan; 16.06.2012
comment
@ Иллан каким-то образом нашел способ ... я обновлю ответ через минуту. - person VJAI; 16.06.2012
comment
@Ilan Ilan Я обновил ответ и похоже, что это не так просто! - person VJAI; 16.06.2012
comment
спасибо за все это. Завтра попробую и обновлю здесь. MVC явно не был разработан с учетом сложных ValidationResults. Вместо того, чтобы повторять вещи, которые делает фреймворк и которые изменятся со следующим обновлением, я могу просто придерживаться того хака, который у меня есть — по крайней мере, он прост. Кстати, какой источник MVC вы смотрели? 3 или 4? - person Ilan; 17.06.2012