2009-10-13 1 views
27

Я широко использую проверку аннотаций данных в ASP.NET MVC 2. Эта новая функция была огромной экономией времени, так как теперь я могу определить как проверку на стороне клиента, так и проверку на стороне сервера в одном месте. Тем не менее, пока я проводил детальное тестирование, я понял, что для кого-то довольно легко обойти проверку на стороне сервера, если я полагаюсь только на проверку аннотации данных. Например, если я определил требуемое поле, аннотируя свойство с атрибутом [Обязательный] и поместив текстовое поле для этого обязательного поля в форме, пользователь может просто удалить текстовое поле из DOM (которое легко можно сделать через Firebug) и теперь валидация аннотации данных не будет активирована в этом свойстве во время ModelBinding внутри Контроллера. Чтобы убедиться, что «требуемая» проверка активирована, я могу повторить проверку после ModelBinding, но тогда я бы повторил свою логику проверки.ASP.NET MVC: достаточно ли проверять аннотацию данных?

Какая рекомендация по проверке? Является ли проверка аннотации данных достаточной? Или нужно повторить проверку, чтобы убедиться, что проверки активируются во всех ситуациях?

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

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

+0

ответ Koritnik в ответ на ваш следить за запрос. Я делаю свою проверку аналогично тому, как он писал. Такое же определение DataAnnotation может использоваться как для проверки сервера, так и для клиента. –

+1

Проверка аннотации данных прекрасна, если вам необходимы подходящие атрибуты проверки и сама структура. Поведение с Обязательным было изменено для RTM-среды ASP.NET MVC 2 из-за обратной связи с сообществом, поэтому [Обязательный] теперь работает так, как вы ожидали. Оптимально, проверьте: блок проверки (корпоративная библиотека), xVal, NHibernate Validators (предположительно не имеет зависимости от ORM NHibernate). – miha

+0

'' Я собираюсь опубликовать этот последующий комментарий в качестве отдельного вопроса, поскольку он представляет собой другой вопрос, чем оригинальный ».« Ссылка на это не будет плохой идеей, а? – Sinjai

ответ

18

Я думаю, быть бдительными в отношении безопасности вы должны выбрать чтобы вы сделали валидацию сервера приоритетом и убедитесь, что это всегда ваш резерв. Проверка вашего сервера должна работать без проверки клиента. Валидация клиента больше для UX, и это имеет первостепенное значение для вашего дизайна, это вторично для безопасности. Имея это в виду, вы обнаружите, что повторяете свою проверку. Цель часто заключается в том, чтобы спроектировать ваше приложение, чтобы можно было максимально интегрировать проверку сервера и клиента, чтобы уменьшить работу, требуемую для проверки на сервере и клиенте. Но будьте уверены, вы должны сделать то и другое.

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

С описанным сценарием должен быть достаточным метод атрибутов DataAnnotation. Кажется, вам просто нужно сделать несколько изменений кода, чтобы гарантировать, что проверка вашего сервера вызывается также при отправке формы.

2

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

Вы можете, однако, настроить модель DataAnnotation самостоятельно, чтобы убедиться, что свойства с [Обязательно] ДОЛЖНЫ быть опубликованы. (сегодня будет следить за кодом).

UPDATE Получить источник DataAnnotations Model Binder и найти эту строку в DataAnnotationsModelBinder.cs

// Only bind properties that are part of the request 
if (bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey)) { 

изменить его на

// Only bind properties that are part of the request 
bool contextHasKey = bindingContext.ValueProvider.DoesAnyKeyHavePrefix(fullPropertyKey); 
bool isRequired = GetValidationAttributes(propertyDescriptor).OfType<RequiredAttribute>().Count() > 0; 
if (contextHasKey || (!contextHasKey && isRequired)) { 
+0

Спасибо Martijn. Я с нетерпением жду вашего кода. –

+0

Несомненно, я разместил это до того, как ушел на работу (сейчас работаю), поэтому нет времени на код :(Я немного модифицировал связующее, так как он не проверял вложенные объекты и не возвращал недопустимые свойства в null, которые я не соглашался с ними http://stackoverflow.com/questions/820468/how-does-dataannotationsmodelbinder-work-with-custom-viewmodels/864541#864541 Я также добавил необходимые проверки, но хотел бы проверить их, когда я вернусь домой –

+0

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

7

Я связал xVal с DataAnnotations и написал свой собственный фильтр действий, который проверяет все параметры типа Entity для целей проверки. Поэтому, если какое-либо поле отсутствует в postback, этот валидатор заполнит словарь ModelState, следовательно, недействителен модель.

Предпосылки:

  • моя сущность/модель все объекты реализуют IObjectValidator интерфейс, объявляющий Validate() метод.
  • мой класс атрибут называется ValidateBusinessObjectAttribute
  • проверки xVal библиотека

Код Действие фильтра:

public void OnActionExecuting(ActionExecutingContext filterContext) 
{ 
    IEnumerable<KeyValuePair<string, object>> parameters = filterContext.ActionParameters.Where<KeyValuePair<string, object>>(p => p.Value.GetType().Equals(this.ObjectType ?? p.Value.GetType()) && p.Value is IObjectValidator); 
    foreach (KeyValuePair<string, object> param in parameters) 
    { 
     object value; 
     if ((value = param.Value) != null) 
     { 
      IEnumerable<ErrorInfo> errors = ((IObjectValidator)value).Validate(); 
      if (errors.Any()) 
      { 
       new RulesException(errors).AddModelStateErrors(filterContext.Controller.ViewData.ModelState, param.Key); 
      } 
     } 
    } 
} 

Мои действия контроллера определяется как это, то:

[ValidateBusinessObject] 
public ActionResult Register(User user, Company company, RegistrationData registrationData) 
{ 
    if (!this.ModelState.IsValid) 
    { 
     return View(); 
    } 
    ... 
} 
+0

У вас есть более подробный пример того, как использовать этот или загружаемый проект, возможно, –

+0

@geocine: Где, по-видимому, проблема? Вы используете MVC1? Новые версии не требуют этого, потому что они автоматически проверяют сильные параметры типа ... Но этот пример здесь так же детализирован, как и должен быть на самом деле. Так где же проблема? –

+0

Я только что прошел мимо, и я новичок в чтении aspmvc о проблемах проверки. Я забыл, что использовал MVC 2. плохой. –

2

я написал мой собственный ValidationService для MVC 1.0, скопировав шаблоны из обоих xV al's DataAnnotationsRuleProvider и Microsoft DataAnnotationsModelBinder (и комментарии Martijn). Сервисный интерфейс ниже:

public interface IValidationService 
{ 
    void Validate(object instance); 

    IEnumerable<ErrorInfo> GetErrors(object instance); 
} 

public abstract class BaseValidationService : IValidationService 
{ 
    public void Validate(object instance) 
    { 
     var errors = GetErrors(instance); 

     if (errors.Any()) 
      throw new RulesException(errors); 
    } 

    public abstract IEnumerable<ErrorInfo> GetErrors(object instance); 
} 

служба является проверка бегун, который ходит свойство дерева экземпляра объекта он получает и фактически выполняет проверки атрибутов, которые он находит на каждой собственности, создание списка объектов ErrorInfo когда атрибуты недействительны. (Я бы опубликовал весь источник, но он был написан для клиента, и я пока не знаю, разрешено ли мне это делать.)

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

Есть несколько других ошибок, которые вы должны знать:

  • Значение по умолчанию DataTypeAttribute в данных аннотации фактически не делать какие-либо проверки типа данных, так что вам нужно писать новый атрибут, который фактически использует xVal regular выражения (или что-то еще) до выполняют тип данных на стороне сервера проверка.
  • xVal не ходит свойства для создания на стороне клиента проверки, так что вы можете сделать некоторые изменения там, чтобы получить более надежную проверку на стороне клиента.

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

1

См Codeproject Server-side Input Validation using Data Annotations

Проверка ввода может быть сделано автоматически на стороне клиента в ASP.NET MVC или явно проверки модели против правил. В этом совете описано, как это можно сделать вручную на стороне сервера приложений ASP.NET или в коде репозитория приложений WPF .

 // Use the ValidationContext to validate the Product model against the product data annotations 
     // before saving it to the database 
     var validationContext = new ValidationContext(productViewModel, serviceProvider: null, items:null); 
     var validationResults = new List<ValidationResult>(); 

     var isValid = Validator.TryValidateObject(productViewModel, validationContext,validationResults, true);