На основании Вашего запроса эти примеры могут содержать грубую лексику.
На основании Вашего запроса эти примеры могут содержать разговорную лексику.
недопустимое значение
Значение не является допустимым
MC3013: SynchronousMode property value is not valid. Valid values are Async and Sync.
MC3013: недопустимое значение свойства SynchronousMode. Разрешены значения Async и Sync.
Internal table type value is not valid. This is an internally inconsistent condition.
Недопустимое значение типа внутренней таблицы. Это внутренне несогласованное состояние.
» value is not valid because it contains no animations.
Cannot open package because FileMode or FileAccess value is not valid for the stream.
» KeyTime value is not valid for key frame at index of this’ ‘ because it is greater than animation’s Duration value ».
Значение KeyTime не является допустимым для опорного кадра, соответствующего индексу для этого, так как оно больше, чем значение Duration для анимации.
» value is not valid because VisualTriggers do not support TemplateBinding.
The specified variable value is not valid for the selected variable type:
SeekOrigin value is not valid. Only SeekOrigin.Begin and SeekOrigin.Current are valid.
Недопустимое значение SeekOrigin. Допустимыми являются только значения SeekOrigin.Begin и SeekOrigin.Current.
The required’ ‘ attribute value is not valid.
The log file size value is not valid.
The HandoffBehavior value is not valid.
Value is not valid for the specified GUID.
Localization comment value is not valid for target property’ ‘ in string ».
» value is not valid. It must contain one, two, or four delimited Lengths.
Недопустимое значение. Оно должно содержать одно, два или четыре значения Length с разделителями.
Результатов: 14. Точных совпадений: 14. Затраченное время: 38 мс
Предложения с «not valid»
They are not valid excuses for failing to do the right thing for our children, which is to start middle and high schools no earlier than 8:30am. |
Это не веские причины отказаться от правильных идей, от которых выиграют наши дети, а именно от начала занятий в школах не ранее 8:30 утра. |
Therefore, the reference to the 1992-1994 period was not valid . |
Поэтому ссылка на период 1992 — 1994 годов ничем не обоснована. |
The offer made to Lebanon is not valid . |
Предлагаемый Ливану вариант не имеет силы. |
A late objection, even if it was not valid , was always an objection. |
Последующее возражение, даже если оно не действительно, всегда остается возражением. |
Thus, the complaints made about the Myanmar Citizenship Law are not valid . |
Таким образом, жалобы в отношении Закона о гражданстве Мьянмы не являются обоснованными. |
Any religious or customary marriage which did not comply with the Marriage Act’s provisions was not valid . |
Любой религиозный или традиционный брак, который не соответствует положениям Закона о браке, является недействительным . |
SeekOrigin value is not valid . Only SeekOrigin.Begin and SeekOrigin.Current are valid . |
Недопустимое значение SeekOrigin. Допустимыми являются только значения SeekOrigin.Begin и SeekOrigin.Current. |
The specified property is not valid . Verify the type of the argument. |
Указанное свойство недопустимо. Проверьте тип аргумента. |
The file name property is not valid . The file name is a device or contains invalid characters. |
Недопустимое свойство имени файла. Имя файла является именем устройства или содержит недопустимые символы. |
The WSDL binding named is not valid because an operation binding doesn’t have a name specified. |
Привязка WSDL с именем недопустима, так как для привязки операции не указано имя. |
The requested TextDecorationCollection string is not valid : ». |
Запрошенная строка TextDecorationCollection не является допустимой:. |
Template can have only a single root element.’ ‘ is not valid . |
Template может иметь только один корневой элемент. не является допустимым. |
Attribute in ‘item’ not valid ; ‘repeat’ expected. |
Недопустимый атрибут в элементе item; ожидается repeat. |
Rule reference not valid . Must specify ‘uri’ or ‘special’. |
Недопустимая ссылка на правило. Необходимо указать uri или special. |
Zero-length TextRange is not valid for this processor. |
Значение TextRange нулевой длины является недопустимым для данного обработчика. |
Specified ContentPosition is not valid for this element. |
Указанное значение ContentPosition не подходит для данного элемента. |
Token is not valid because it is more than 250 characters. |
Маркер является недействительным , поскольку содержит более 250 знаков. |
Value is not valid for the specified GUID. |
Значение не является допустимым для указанного GUID. |
Default value for’ ‘ property is not valid because ValidateValueCallback failed. |
Значение по умолчанию свойства не является допустимым, так как произошел сбой вызова ValidateValueCallback . |
Layout information on TextView is not valid . Must call Validate before calling this method. |
Недопустимые сведения о макете для TextView. Перед вызовом данного метода необходимо вызвать Validate . |
Object metadata stream in the package is corrupt and the content is not valid . |
Поток метаданных объекта в пакете поврежден, имеется недопустимое содержимое. |
Internal table type value is not valid . This is an internally inconsistent condition. |
Недопустимое значение типа внутренней таблицы. Это внутренне несогласованное состояние. |
ClipToBounds is not valid for Window. |
Значение ClipToBounds не является допустимым для Window. |
Parameter and value pair is not valid . Expected form is parameter=value. |
Неверная пара параметра и значения. Ожидается формат параметр=значение. |
Cannot open package because FileMode or FileAccess value is not valid for the stream. |
Не удается открыть пакет, так как значение FileMode или FileAccess не является допустимым для потока. |
Locator part is not valid . Missing or malformed name/value pair: ». |
Недопустимая часть локатора. Отсутствует или неверно сформирована пара имя — значение:. |
Override tag is not valid per the schema. Verify that attributes are correct. |
Переопределяющий тег является недопустимым согласно данной схеме. Проверьте правильность атрибутов. |
Markup extensions are not allowed for Uid or Name property values, so’ ‘ is not valid . |
Для значений свойств Uid или Name не допускаются расширения разметки, поэтому не является допустимым значением. |
DataObject or data is not valid for conversion to OLE data. |
DataObject или данные являются недопустимыми для преобразования в данные OLE. |
ContentType string is not valid . Expected format is type/subtype. |
Недопустимая строка ContentType. Ожидаемый формат: тип/подтип. |
Character offset is not valid in document with characters. |
Смещение символов недопустимо в документе с символами. |
Number of samples in data block not valid . |
Недопустимое число примеров в блоке данных. |
Types tag has attributes not valid per the schema. |
Тег типов имеет недопустимые атрибуты, согласно схеме. |
The digital signature is not valid . The contents of the package have been modified. |
Недействительная цифровая подпись. Содержимое пакета было изменено. |
The key collection includes entry that is not valid . It must be an IBamlDictionaryKey. |
Семейство ключей включает недопустимую запись. Требуется IBamlDictionaryKey. |
The key decrypted from the file header is not valid . |
Недопустимый ключ, дешифрованный из заголовка файла. |
The defendant argued that the document was not valid as it was not signed. |
Ответчик считает, что документ недействителен , так как в документе отсутствует его подпись. |
Hence, the observer’s allegation that the draft law was not open for public comment was not valid . |
Следовательно, утверждение наблюдателя о том, что общественности не была предоставлена возможность высказать замечания по законопроекту, лишено оснований. |
The version number in the package is not valid . The version number cannot be greater than current version number. |
Недопустимый номер версии в пакете. Номер версии не может быть больше номера текущей версии. |
The configuration file loaded, but is not valid . The file is not formatted correctly, may be missing an element, or may be damaged. |
Файл конфигурации загружен, но является недопустимым. Файл имеет неправильный формат, возможно, содержит отсутствующий элемент или поврежден. |
Either the source object or the destination object is not valid . |
Неверно заданный исходный или целевой объект. |
The binary encoder session is not valid . There was an error decoding a previous message. |
Недопустимый сеанс двоичного кодировщика. Ошибка декодирования предыдущего сообщения. |
Invalid Audio header info. Cannot calculate data block size because blockAlign or metadata size not valid . |
Недопустимые сведения заголовка звука. Не удается вычислить размер блока данных из — за недопустимого размера blockAlign или метаданных. |
The address in the From line is malformed. It is either missing the symbol or is not valid . |
Некорректный адрес в строке От. Отсутствует знак или адрес недопустим. |
The object name is not valid . The name cannot be empty. |
Недопустимое имя объекта. Имя не может быть пустым. |
If you select a ship or receipt date that is not valid , the Simulate delivery dates form is displayed so that you can select alternative dates. |
При выборе недопустимой даты отгрузки или поступления будет открыта форма Моделирование даты поставки, в которой можно выбрать альтернативные даты. |
The email address or password you entered is not valid . |
Введенный адрес электронной почты или пароль недействителен . |
The counter-argument — that any sell-off or failure by China to continue to buy US government securities would hurt China as much as America — is not valid . |
Встречный довод о том, что любая распродажа и прекращение дальнейшего приобретения Китаем государственных ценных бумаг США повредит Китаю так же сильно, как Америке, уже не является убедительным. |
They are not valid for Cash, Cheque, ATM, or any other method of funds transfer. |
Они не применимы к операциям с наличными, чеками, банкоматами или для любых других способов перевода средств. |
It seems, then, that the proceedings in support of survival of firms permit Mexican law are not valid in the land of stars and stripes, contrary to international conventions. |
Кажется, что процедуры в поддержку выживания фирм, которые дозволены мексиканскими законами, не являются действительными в стране полос и звезд, в противоречии с международными соглашениями. |
Yes, and they’re not valid until they bear my signature and my seal. |
Да, но к сожалению, без моей подписи оно недействительно . |
I feel that since google links are often not valid , they should be discouraged. |
Я считаю, что, поскольку ссылки google часто не являются действительными, они должны быть обескуражены. |
A Schengen visa, even one issued by France, is not valid for these territories. |
Шенгенская виза, даже выданная Францией, не действительна для этих территорий. |
For small values of the shape parameter, the algorithms are often not valid . |
При малых значениях параметра shape алгоритмы часто оказываются неверными. |
Self-promotion, autobiography, product placement and most paid material are not valid routes to an encyclopedia article. |
Самореклама, автобиография, продакт — плейсмент и большинство платных материалов не являются допустимыми маршрутами к энциклопедической статье. |
Out of state permits not valid in Connecticut, but non-residents may apply for a Connecticut non-resident carry permit through the mail. |
Внегосударственные разрешения не действительны в Коннектикуте,но нерезиденты могут подать заявку на получение внегосударственного разрешения по почте. |
Some European countries require adults to carry proof of identity at all times, but a driving permit is not valid for identification in every European country. |
Некоторые европейские страны требуют от взрослых постоянно иметь при себе удостоверение личности, но водительское удостоверение не является действительным для идентификации в каждой европейской стране. |
Single Journey fare are not valid for transfers. |
Для трансферов не действует единый тариф проезда. |
If animal is not valid , I suggest to remove all animal-based National personification since it is about person. |
Если животное не является действительным, я предлагаю удалить всю основанную на животных национальную персонификацию, так как речь идет о человеке. |
This, however, implies that AaB does not entail AiB, and some of the syllogisms mentioned above are not valid when there are no A’s. |
Это, однако, подразумевает, что AaB не влечет за собой AiB, и некоторые из силлогизмов, упомянутых выше, не являются действительными, когда нет никаких а. |
Валидация форм и полей¶
Валидация формы происходит при очистке данных. Если вы хотите настроить этот процесс, есть различные места для внесения изменений, каждое из которых служит для разных целей. В процессе обработки формы выполняются три типа методов очистки. Обычно они выполняются, когда вы вызываете метод is_valid()
на форме. Есть и другие вещи, которые также могут вызвать очистку и проверку (обращение к атрибуту errors
или прямой вызов full_clean()
), но обычно они не нужны.
В общем, любой метод очистки может поднять ValidationError
, если есть проблема с данными, которые он обрабатывает, передавая соответствующую информацию конструктору ValidationError
. See below для лучшей практики поднятия ValidationError
. Если не поднимается ValidationError
, метод должен вернуть очищенные (нормализованные) данные в виде объекта Python.
Большинство валидаций можно выполнить с помощью validators — помощников, которые можно использовать повторно. Валидаторы — это функции (или callables), которые принимают один аргумент и вызывают ValidationError
при недопустимом вводе. Валидаторы запускаются после вызова методов to_python
и validate
поля.
Валидация формы разбита на несколько этапов, которые можно настроить или отменить:
-
Метод
to_python()
наField
является первым шагом в каждой валидации. Он преобразует значение к правильному типу данных и выдает сообщениеValidationError
, если это невозможно. Этот метод принимает необработанное значение от виджета и возвращает преобразованное значение. Например,FloatField
превратит данные в Pythonfloat
или выдастValidationError
. -
Метод
validate()
наField
обрабатывает специфическую для поля валидацию, которая не подходит для валидатора. Он принимает значение, которое было приведено к правильному типу данных, и при любой ошибке выдает сообщениеValidationError
. Этот метод ничего не возвращает и не должен изменять значение. Вы должны переопределить его для обработки логики валидации, которую вы не можете или не хотите поместить в валидатор. -
Метод
run_validators()
на полеField
запускает все валидаторы поля и объединяет все ошибки в одинValidationError
. Вам не нужно переопределять этот метод. -
Метод
clean()
в подклассеField
отвечает за выполнениеto_python()
,validate()
иrun_validators()
в правильном порядке и распространение их ошибок. Если в любой момент времени какой-либо из методов вызывает ошибкуValidationError
, валидация останавливается, и эта ошибка выдается. Этот метод возвращает чистые данные, которые затем вставляются в словарьcleaned_data
формы. -
Метод
clean_<fieldname>()
вызывается на подклассе формы – где<fieldname>
заменяется на имя атрибута поля формы. Этот метод выполняет любую очистку, специфичную для данного атрибута, не связанную с типом поля, которым он является. Этому методу не передаются никакие параметры. Вам нужно будет найти значение поля вself.cleaned_data
и помнить, что в этот момент это будет объект Python, а не исходная строка, представленная в форме (она будет вcleaned_data
, потому что метод general fieldclean()
, описанный выше, уже однажды очистил данные).Например, если вы хотите проверить, что содержимое
CharField
под названиемserialnumber
является уникальным,clean_serialnumber()
будет подходящим местом для этого. Вам не нужно конкретное поле (этоCharField
), но вам нужен специфический для поля формы фрагмент проверки и, возможно, очистки/нормализации данных.Возвращаемое значение этого метода заменяет существующее значение в
cleaned_data
, поэтому это должно быть значение поля изcleaned_data
(даже если этот метод не изменил его) или новое очищенное значение. -
Метод
clean()
подкласса формы может выполнять валидацию, требующую доступа к нескольким полям формы. Сюда можно отнести такие проверки, как «если полеA
предоставлено, то полеB
должно содержать действительный адрес электронной почты». При желании этот метод может вернуть совершенно другой словарь, который будет использован в качествеcleaned_data
.Поскольку методы валидации полей были запущены к моменту вызова
clean()
, у вас также есть доступ к атрибутуerrors
формы, который содержит все ошибки, возникшие при очистке отдельных полей.Обратите внимание, что любые ошибки, возникающие при переопределении
Form.clean()
, не будут связаны с каким-либо конкретным полем. Они попадают в специальное «поле» (называемое__all__
), к которому вы можете получить доступ через методnon_field_errors()
, если вам это необходимо. Если вы хотите прикрепить ошибки к определенному полю формы, вам нужно вызватьadd_error()
.Также обратите внимание, что существуют особые соображения при переопределении метода
clean()
подклассаModelForm
. (см. ModelForm documentation для получения дополнительной информации)
Эти методы выполняются в указанном выше порядке, по одному полю за раз. То есть, для каждого поля формы (в порядке их объявления в определении формы) выполняется метод Field.clean()
(или его переопределение), затем clean_<fieldname>()
. Наконец, когда эти два метода выполнены для каждого поля, выполняется метод Form.clean()
, или его переопределение, независимо от того, вызвали ли предыдущие методы ошибки.
Примеры каждого из этих методов приведены ниже.
Как уже упоминалось, любой из этих методов может вызвать ошибку ValidationError
. Для любого поля, если метод Field.clean()
вызывает ValidationError
, любой метод очистки, специфичный для данного поля, не вызывается. Однако методы очистки для всех оставшихся полей все равно выполняются.
Поднятие ValidationError
¶
Чтобы сделать сообщения об ошибках гибкими и легко переопределяемыми, примите во внимание следующие рекомендации:
-
Предоставить описательную ошибку
code
конструктору:# Good ValidationError(_('Invalid value'), code='invalid') # Bad ValidationError(_('Invalid value'))
-
Не вставляйте переменные в сообщение; используйте заполнители и аргумент
params
конструктора:# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError(_('Invalid value: %s') % value)
-
Используйте ключи отображения вместо позиционного форматирования. Это позволяет располагать переменные в любом порядке или вообще их не использовать при переписывании сообщения:
# Good ValidationError( _('Invalid value: %(value)s'), params={'value': '42'}, ) # Bad ValidationError( _('Invalid value: %s'), params=('42',), )
-
Оберните сообщение символом
gettext
, чтобы включить перевод:# Good ValidationError(_('Invalid value')) # Bad ValidationError('Invalid value')
Собираем все вместе:
raise ValidationError( _('Invalid value: %(value)s'), code='invalid', params={'value': '42'}, )
Следование этим рекомендациям особенно необходимо, если вы пишете многократно используемые формы, поля форм и поля моделей.
Хотя это и не рекомендуется, если вы находитесь в конце цепочки валидации (т.е. ваша форма clean()
метод) и вы знаете, что вам никогда не понадобится переопределять сообщение об ошибке, вы можете выбрать менее многословный вариант:
ValidationError(_('Invalid value: %s') % value)
Методы Form.errors.as_data()
и Form.errors.as_json()
значительно выигрывают от полнофункциональных ValidationError
s (с code
именем и params
словарем).
Возникновение множества ошибок¶
Если вы обнаружили несколько ошибок во время работы метода очистки и хотите сигнализировать обо всех из них отправителю формы, можно передать список ошибок конструктору ValidationError
.
Как и выше, рекомендуется передавать список экземпляров ValidationError
с code
s и params
, но подойдет и список строк:
# Good raise ValidationError([ ValidationError(_('Error 1'), code='error1'), ValidationError(_('Error 2'), code='error2'), ]) # Bad raise ValidationError([ _('Error 1'), _('Error 2'), ])
Использование валидации на практике¶
В предыдущих разделах объяснялось, как работает валидация в целом для форм. Поскольку иногда бывает проще понять, как работает каждая функция, здесь приведена серия небольших примеров, в которых используется каждая из предыдущих функций.
Использование валидаторов¶
Поля формы (и модели) Django поддерживают использование полезных функций и классов, известных как валидаторы. Валидатор — это вызываемый объект или функция, которая принимает значение и не возвращает ничего, если значение действительно, или выдает ошибку ValidationError
, если нет. Они могут быть переданы в конструктор поля через аргумент validators
или определены в самом классе Field
с помощью атрибута default_validators
.
Валидаторы могут использоваться для проверки значений внутри поля, давайте посмотрим на Django’s SlugField
:
from django.core import validators from django.forms import CharField class SlugField(CharField): default_validators = [validators.validate_slug]
Как вы можете видеть, SlugField
— это CharField
с настроенным валидатором, который проверяет, что отправленный текст соответствует некоторым правилам символов. Это также можно сделать при определении поля так:
эквивалентно:
slug = forms.CharField(validators=[validators.validate_slug])
Обычные случаи, такие как проверка по электронной почте или регулярному выражению, могут быть обработаны с помощью существующих классов валидаторов, доступных в Django. Например, validators.validate_slug
— это экземпляр RegexValidator
, построенный с первым аргументом в виде шаблона: ^[-a-zA-Z0-9_]+$
. Смотрите раздел writing validators, чтобы увидеть список того, что уже доступно, и пример того, как написать валидатор.
Очистка полей формы по умолчанию¶
Давайте сначала создадим поле пользовательской формы, которое проверяет, что его входные данные — это строка, содержащая адреса электронной почты, разделенные запятыми. Полный класс выглядит следующим образом:
from django import forms from django.core.validators import validate_email class MultiEmailField(forms.Field): def to_python(self, value): """Normalize data to a list of strings.""" # Return an empty list if no input was given. if not value: return [] return value.split(',') def validate(self, value): """Check if value consists only of valid emails.""" # Use the parent's handling of required fields, etc. super().validate(value) for email in value: validate_email(email)
В каждой форме, использующей это поле, эти методы будут выполняться до того, как с данными поля можно будет сделать что-либо еще. Это очистка, специфичная для данного типа поля, независимо от того, как оно будет использоваться в дальнейшем.
Давайте создадим ContactForm
, чтобы продемонстрировать, как вы будете использовать это поле:
class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() recipients = MultiEmailField() cc_myself = forms.BooleanField(required=False)
Используйте MultiEmailField
как любое другое поле формы. Когда на форме будет вызван метод is_valid()
, в процессе очистки будет запущен метод MultiEmailField.clean()
, который, в свою очередь, вызовет пользовательские методы to_python()
и validate()
.
Очистка определенного атрибута поля¶
Продолжая предыдущий пример, предположим, что в нашем ContactForm
мы хотим убедиться, что поле recipients
всегда содержит адрес "fred@example.com"
. Это проверка, специфичная для нашей формы, поэтому мы не хотим помещать ее в общий класс MultiEmailField
. Вместо этого мы напишем метод очистки, который работает с полем recipients
, следующим образом:
from django import forms from django.core.exceptions import ValidationError class ContactForm(forms.Form): # Everything as before. ... def clean_recipients(self): data = self.cleaned_data['recipients'] if "fred@example.com" not in data: raise ValidationError("You have forgotten about Fred!") # Always return a value to use as the new cleaned data, even if # this method didn't change it. return data
Очистка и проверка полей, которые зависят друг от друга¶
Предположим, мы добавим еще одно требование к нашей контактной форме: если поле cc_myself
является True
, то subject
должно содержать слово "help"
. Мы выполняем проверку более чем одного поля одновременно, поэтому метод формы clean()
является хорошим местом для этого. Обратите внимание, что здесь мы говорим о методе clean()
на форме, тогда как ранее мы писали метод clean()
на поле. Важно четко различать поля и формы, когда мы решаем, где проводить валидацию. Поля — это отдельные точки данных, а формы — это набор полей.
К моменту вызова метода clean()
формы будут запущены все методы очистки отдельных полей (предыдущие два раздела), поэтому self.cleaned_data
будет заполнен любыми данными, которые сохранились до сих пор. Поэтому вам также нужно помнить о том, что поля, которые вы хотите проверить, могут не выдержать первоначальной проверки отдельных полей.
Есть два способа сообщить о любых ошибках на этом этапе. Вероятно, самый распространенный способ — вывести ошибку в верхней части формы. Чтобы создать такую ошибку, вы можете поднять ValidationError
из метода clean()
. Например:
from django import forms from django.core.exceptions import ValidationError class ContactForm(forms.Form): # Everything as before. ... def clean(self): cleaned_data = super().clean() cc_myself = cleaned_data.get("cc_myself") subject = cleaned_data.get("subject") if cc_myself and subject: # Only do something if both fields are valid so far. if "help" not in subject: raise ValidationError( "Did not send for 'help' in the subject despite " "CC'ing yourself." )
В этом коде, если возникает ошибка валидации, форма выводит сообщение об ошибке в верхней части формы (обычно) с описанием проблемы. Такие ошибки являются не-полевыми ошибками, которые отображаются в шаблоне с помощью {{ form.non_field_errors }}
.
Вызов super().clean()
в коде примера гарантирует, что любая логика валидации в родительских классах будет сохранена. Если ваша форма наследует другую, которая не возвращает словарь cleaned_data
в своем методе clean()
(это необязательно), то не присваивайте cleaned_data
результату вызова super()
и используйте self.cleaned_data
вместо этого:
def clean(self): super().clean() cc_myself = self.cleaned_data.get("cc_myself") ...
Второй подход для сообщения об ошибках валидации может включать присвоение сообщения об ошибке одному из полей. В данном случае давайте присвоим сообщение об ошибке обеим строкам «subject» и «cc_myself» в отображении формы. Будьте осторожны, делая это на практике, так как это может привести к запутанному выводу формы. Мы показываем, что здесь возможно, и предоставляем вам и вашим дизайнерам самим решать, что будет эффективно работать в вашей конкретной ситуации. Наш новый код (заменяющий предыдущий пример) выглядит следующим образом:
from django import forms class ContactForm(forms.Form): # Everything as before. ... def clean(self): cleaned_data = super().clean() cc_myself = cleaned_data.get("cc_myself") subject = cleaned_data.get("subject") if cc_myself and subject and "help" not in subject: msg = "Must put 'help' in subject when cc'ing yourself." self.add_error('cc_myself', msg) self.add_error('subject', msg)
Вторым аргументом add_error()
может быть строка или, предпочтительно, экземпляр ValidationError
. Более подробную информацию смотрите в Поднятие ValidationError. Обратите внимание, что add_error()
автоматически удаляет поле из cleaned_data
.
Пользователи часто передают в приложение некорректные данные. Такое происходит либо из злого умысла, либо по ошибке. Сто́ит проверять данные на соответствие бизнес-требованиям.
Эти бизнес-правила влияют на каждый уровень приложения. Веб-интерфейс сообщает пользователю подробные и локализованные сообщения об ошибках. Уровни бизнес-логики и хранения должны проверять приходящие от клиентов значения, перед отправкой в хранилище. База данных SQL делает окончательную проверку, чтобы гарантировать целостность хранимой информации.
Эти задачи поможет решить Bean Validation. Он интегрирован со Spring и Spring Boot. Hibernate Validator считается эталонной реализацией Bean Validation.
Идея Bean Validation в том, чтобы определять такие правила, как «Это поле не может быть null» или «Это число должно находиться в заданном диапазоне» с помощью аннотаций. Это гораздо проще, чем постоянно писать условные операторы проверок.
Hibernate Validator также задаёт правила валидации с помощью аннотаций над полями класса. Этот декларативный подход не загрязняет код. При передаче размеченного таким образом объекта класса в валидатор, происходит проверка на ограничения.
Добавьте стартер в проект, чтобы включить валидацию:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Спонсор поста
Используемые версии
Java 17
Spring Boot 2.7.1
История изменений статьи
26.02.2022: Обновил версию Java с 11 до 17. Также обновил версию Spring Boot до 2.6.3
29.06.2022: Spring Boot – 2.7.1. Добавил коллекцию Postman с тестами.
Валидация в контроллерах
Обычно данные сначала попадают в контроллер. У входящего HTTP запроса возможно проверить следующие параметры:
- тело запроса.
- переменные пути (например,
id
в/foos/{id}
). - параметры запроса.
Рассмотрим каждый из них подробнее.
Валидация тела запроса
Тело запроса POST
и PUT
обычно содержит данные в формате JSON. Spring автоматически сопоставляет входящий JSON с объектом Java.
Разметим сущность с помощью аннотаций валидации.
public class PersonDto {
private Long id;
@NotBlank
private String name;
@Min(1)
@Max(10)
private int numberBetweenOneAndTen;
@Pattern(regexp = "^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$")
private String ipAddress;
// getters and setters
}
Все основные аннотации мы рассмотрим позднее, но по названиям довольно легко понять, какое условие они проверяют:
- Поле
name
не должно быть пустым илиnull
. - Поле
numberBetweenOneAndTen
должно́ находиться в диапазоне от 1 до 10, включительно. - Поле
ipAddress
должно содержать строку в формате IP-адреса.
Достаточно добавить для входящего параметра personDto
аннотацию @Valid
, чтобы передать объект в валидатор. Выполнение метода контролера начнётся только, если объект пройдёт все проверки.
@RestController
@RequestMapping("/api/person")
public class PersonController {
@PostMapping
public ResponseEntity<String> valid(@Valid @RequestBody PersonDto personDto) {
return ResponseEntity.ok("valid");
}
}
Вызываем наш POST метод и передаём в него не валидные данные.
Postman возвращает нам ошибку, а в консоли видим исключение. Оно сообщает нам о двух ошибках валидации.
Исключение MethodArgumentNotValidException
выбрасывается, когда объект не проходит проверку. По умолчанию Spring преобразует это исключение в HTTP статус 400.
Исключение информативное, но тяжёлое для восприятия. Пользователь не получает никакой информации об ошибке. Далее мы рассмотрим, как это исправить.
Проверка переменных пути и параметров запроса
При проверке переменных пути и параметров запроса не проверяются сложные Java-объекты, так как path-переменные и параметры запроса являются примитивными типами, такими как int
, или их аналогами: Integer
или String
.
Вместо аннотации поля класса, как описано выше, добавляют аннотацию ограничения (в данном случае @Min
) непосредственно к параметру метода в контроллере:
@Validated
@RestController
@RequestMapping("/api/person")
public class PersonController {
@GetMapping("{id}")
public ResponseEntity<String> getById(
@PathVariable("id") @Min(0) int personId
) {
return ResponseEntity.ok("valid");
}
@GetMapping
public ResponseEntity<String> getByName(
@RequestParam("name") @NotBlank String name
) {
return ResponseEntity.ok("valid");
}
}
Обратите внимание, что необходимо добавить @Validated
в контроллер на уровне класса, чтобы проверять параметры метода. В этом случае аннотация @Validated
устанавливается на уровне класса, даже если она присутствует на методах.
В отличии валидации тела запроса, при неудачной проверки параметра вместо метода MethodArgumentNotValidException
будет выброшен ConstraintViolationException
. По умолчанию последует ответ со статусом HTTP 500 (Internal Server Error), так как Spring не регистрирует обработчик для этого исключения по умолчанию.
Валидация в сервисном слое
Можно проверять данные на любых других компонентах Spring. Для этого используется комбинация аннотаций @Validated
и @Valid
.
@Service
@Validated
public class PersonService {
public void save(@Valid PersonDto personDto) {
// do something
}
}
Напомню, как выглядит наша сущность:
public class PersonDto {
private Long id;
@NotBlank
private String name;
@Min(1)
@Max(10)
private int numberBetweenOneAndTen;
@Pattern(regexp = "^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$")
private String ipAddress;
// getters and setters
}
Казалось бы, пример такой же как и в контроллере и логично ожидать MethodArgumentNotValidException
, но будет выброшен ConstraintViolationException
и 500 ошибка.
Проверка аргументов метода
Помимо объкетов можно проверять примитивы и их обертки, выступающие в виде аргументов метода.
Валидация сущностей JPA
Persistence Layer – это последняя линия проверки данных. По умолчанию Spring Data использует Hibernate, который поддерживает Bean Validation из коробки.
Допустим, необходимо хранить объекты нашего класса PersonDto
в базе данных. Когда репозиторий пытается сохранить не валидный PersonDto
, чьи аннотации ограничений нарушаются, выбрасывается ConstraintViolationException
.
Bean Validation запускается Hibernate только после того как EntityManager
вызовет flush()
.
Для отключения валидации в репозиториях установите свойство Spring Boot spring.jpa.properties.javax.persistence.validation.mode
равным null
.
Где проводить валидацию?
На мой взгляд, лучшее место для основной валидации это сервисный слой. У этого есть несколько причин:
- Сервисы вызывают друг друга. Если сделать всю валидацию на контроллерах, то один сервис сможет передавать невалидные параметры в другой.
- Валидация в репозиторном слое означает, что бизнес-код работал с потенциально невалидными объектами, что может привести к непредвиденным ошибкам. И не у всех сервисов есть этот слой.
- Иногда ваши сервисы взаимодействиую с клиентами не только через контроллеры, что также может привести к работе с невалидными объектами в бизнесовом слое.
Конкретизация ошибок
Когда проверка не удается, лучше вернуть клиенту понятное сообщение об ошибке. Для этого необходимо вернуть структуру данных с сообщением об ошибке для каждой проверки, которая не прошла валидацию.
Я подробно описывал обработку исключений в REST API в отдельной статье. Здесь мы разберем только обработку исключений валидации.
Сначала определим структуру сообщения с ошибкой. Назовем ее ValidationErrorResponse
. И этот класс содержит список объектов Violation
:
@Getter
@RequiredArgsConstructor
public class ValidationErrorResponse {
private final List<Violation> violations;
}
@Getter
@RequiredArgsConstructor
public class Violation {
private final String fieldName;
private final String message;
}
Затем создаем ControllerAdvice
, который обрабатывает все ConstraintViolationExventions
, которые пробрасываются до уровня контроллера. Чтобы отлавливать ошибки валидации и для тел запросов, мы также будем перехватывать и MethodArgumentNotValidExceptions
:
@ControllerAdvice
public class ErrorHandlingControllerAdvice {
@ResponseBody
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ValidationErrorResponse onConstraintValidationException(
ConstraintViolationException e
) {
final List<Violation> violations = e.getConstraintViolations().stream()
.map(
violation -> new Violation(
violation.getPropertyPath().toString(),
violation.getMessage()
)
)
.collect(Collectors.toList());
return new ValidationErrorResponse(violations);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorResponse onMethodArgumentNotValidException(
MethodArgumentNotValidException e
) {
final List<Violation> violations = e.getBindingResult().getFieldErrors().stream()
.map(error -> new Violation(error.getField(), error.getDefaultMessage()))
.collect(Collectors.toList());
return new ValidationErrorResponse(violations);
}
}
Здесь информацию о нарушениях из исключений переводится в нашу структуру данных ValidationErrorResponse
.
Можно изменить сообщение об ошибке с помощью параметра message
у любой аннотации валидации.
@Pattern(
regexp = "^((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\.(?!$)|$)){4}$",
message = "Не соответствует формату IP адреса"
)
private String ipAddress;
Валидация конфигурации приложения
Spring Boot аннотация @ConfigurationProperties
используется для связывания свойств из application.properties
с Java объектом.
Bean Validation поможет обнаружить ошибку в этих данных при старте приложения. Допустим имеется следующий конфигурационный класс:
@Validated
@ConfigurationProperties(prefix="app.properties")
class AppProperties {
@NotEmpty
private String name;
@Min(value = 7)
@Max(value = 30)
private Integer reportIntervalInDays;
@Email
private String reportEmailAddress;
// getters and setters
}
При попытке запуска с недействительным адресом электронной почты получаем ошибку:
***
APPLICATION FAILED TO START
***
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException:
Failed to bind properties under 'app.properties' to
io.reflectoring.validation.AppProperties failed:
Property: app.properties.reportEmailAddress
Value: manager.analysisapp.com
Reason: must be a well-formed email address
Action:
Update your application's configuration
Стандартные ограничения
Библиотека javax.validation
имеет множество аннотаций для валидации.
Аннотации имеют атрибуты, которые позволяют производить более тонкую настройку проверки, но каждая аннотация имеет следующие поля:
message
– указывает на ключ свойства вValidationMessages.properties
, который используется для отправки сообщения в случае нарушения ограничения.groups
– позволяет определить, при каких обстоятельствах будет срабатывать эта проверка. О группах проверки поговорим позже.payload
– позволяет определить полезную нагрузку, которая будет передаваться сс проверкой.@Constraint
– указывает на реализацию интерфейсаConstraintValidator
.
Рассмотрим самые популярные ограничения:
@NotNull
— аннотированный элемент не должен бытьnull
. Принимает любой тип.@Null
— аннотированный элемент должен бытьnull
. Принимает любой тип.@NotBlank
— аннотированный элемент не должен бытьnull
и должен содержать хотя бы один непробельный символ. Принимает CharSequence.@NotEmpty
— аннотированный элемент не должен бытьnull
или пустым. Поддерживаемые типы:CharSequence
Collection
. Оценивается размер коллекцииMap
. Оценивается размер мапыArray
. Оценивается длина массива
@Size
— размер аннотированного элемента должен быть между указанными границами, включая сами границы.null
элементы считаются валидными. Поддерживаемые типы:CharSequence
. Оценивается длина последовательности символовCollection
. Оценивается размер коллекцииMap
. Оценивается размер мапыArray
. Оценивается длина массива
@AssertTrue
проверяет, что аннотированное значение свойства истинно.@Email
подтверждает, что аннотированное свойство является действительным адресом электронной почты.@Positive
и@PositiveOrZero
применяются к числовым значениям и подтверждают, что они строго положительные или положительные, включая 0.@Negative
и@NegativeOrZero
применяются к числовым значениям и подтверждают, что они строго отрицательные или отрицательные, включая 0.@Past
и@PastOrPresent
проверяют, что значение даты находится в прошлом или прошлом, включая настоящее.@Future
и@FutureOrPresent
подтверждают, что значение даты находится в будущем или в будущем, включая настоящее.
Различия межу @NotNull
, @NotEmpty
и @NotBlank
@NotBlank
применяется только к строкам и проверяет, что строка не пуста и не состоит только из пробелов.
@NotNull
применяется к CharSequence
, Collection
, Map
или Array
и проверяет, что объект не равен null
. Но при этом он может быть пуст.
@NotEmpty
применяется к CharSequence
, Collection
, Map
или Array
и проверяет, что он не null
имеет размер больше 0.
Аннотация @Size(min=6)
пропустит строку состоящую из 6 пробелов и/или символов переноса строки, а @NotBlank
не пропустит.
Группы валидаций
Некоторые объекты участвуют в разных вариантах использования. Возьмем типичные операции CRUD: при обновлении и создании, скорее всего, будет использоваться один и тот же класс. Тем не менее, некоторые валидации должны срабатывать при различных обстоятельствах:
- только перед созданием
- только перед обновлением
- или в обоих случаях
Функция Bean Validation, которая позволяет нам внедрять такие правила проверки, называется “Validation Groups”. Все аннотации ограничений имеют поле groups
. Это поле используется для передачи любых классов, каждый из которых определяет группу проверки.
Для нашего примера CRUD определим два маркерных интерфейса OnCreate
и OnUpdate
:
public interface Marker {
interface OnCreate {}
interface OnUpdate {}
}
Затем используем эти интерфейсы с любой аннотацией ограничения:
public class PersonDto {
@Null(groups = Marker.OnCreate.class)
@NotNull(groups = Marker.OnUpdate.class)
private Long id;
// ... ... ... ... ...
}
Это позволит убедиться, что id
пуст при создании и заполнен при обновлении. Spring поддерживает группы проверки только с аннотацией @Validated
@Validated
@RestController
@RequestMapping("/api/group-valid/person")
public class PersonControllerGroupValid {
@PostMapping
@Validated({Marker.OnCreate.class})
public ResponseEntity<String> create(@RequestBody @Valid PersonDto personDto) {
return ResponseEntity.ok("valid");
}
@PutMapping
@Validated(Marker.OnUpdate.class)
public ResponseEntity<String> update(@RequestBody @Valid PersonDto personDto) {
return ResponseEntity.ok("valid");
}
}
Обратите внимание, что аннотация @Validated
применяется ко всему классу. Чтобы определить, какая группа проверки активна, она также применяется на уровне метода.
Использование групп проверки может легко стать анти-паттерном.
При использовании групп валидации сущность должна знать правила валидации для всех случаев использования (групп), в которых она используется.
Создание своего ограничения
Bean Validation не ограничивается встроенными аннотациями, вы можете создавать собственные ограничения и аннотации. Пользовательские ограничения позволяют даже применять аннотации на уровне класса и проверять несколько атрибутов экземпляра класса одновременно.
Напишем свою аннотацию, которая будет проверять, что строка начинается с большой буквы. Сначала создаем аннотацию @CapitalLetter
:
@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = CapitalLetterValidator.class)
@Documented
public @interface CapitalLetter {
String message() default "{CapitalLetter.invalid}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
Реализация валидатора выглядит следующим образом:
public class CapitalLetterValidator implements ConstraintValidator<CapitalLetter, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value != null && !value.isEmpty()) {
return Character.isUpperCase(value.charAt(0));
}
return true;
}
}
Теперь можно использовать аннотацию @CapitalLetter
, как и любую другую аннотацию ограничения.
public class PersonDto {
// ... ... ... ... ...
@NotBlank
@CapitalLetter
private String name;
// ... ... ... ... ...
}
Принудительный вызов валидации
Для принудительного вызова проверки, без использования Spring Boot, создайте валидатор вручную.
public class ProgrammaticallyValidatingService {
public void validateInput(PersonDto personDto) {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<personDto>> violations = validator.validate(personDto);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
Тем не менее, Spring Boot предоставляет предварительно сконфигурированный экземпляр валидатора. Внедрив этот экземпляр в сервис не придется создавать его вручную.
@Service
public class ProgrammaticallyValidatingService {
private Validator validator;
public ProgrammaticallyValidatingService(Validator validator) {
this.validator = validator;
}
public void validateInputWithInjectedValidator(PersonDto personDto) {
Set<ConstraintViolation<PersonDto>> violations = validator.validate(personDto);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(violations);
}
}
}
Резюмирую
Валидация это неотъемлимая часть бизнес логики. Используя зависимость spring-boot-starter-validation
, мы можем облегчить себе работу.
Валидацию делают при переходе данных из одного архитектурного слоя в другой, чтобы не разрушить логику принимаемого слоя. Также можно настроить валидацию конфигурации приложения.
Стоит возвращать клиенту понятное описание ошибки валидации, используя @ControllerAdvice
.