Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
В Web API, как и в MVC, большое значение имеет валидация данных. Однако в Web API она имеет свои особенности.
В первую очередь состояние обработки запроса на сервере мы можем контроллировать с помощью статусных кодов:
200
: статус Ok. Указывает на удачное выполнение запроса
201
: статус Created. Указывает на успешное создание объекта, как правило, используется в запросах POST
204
: статус NoContent - запрос прошел успешно, например, после удаления
400
: статус BadRequest - ошибка при выполнении запроса
401
: статус Unathorized - пользователь не авторизован
403
: статус Forbidden - доступ запрещен
404
: статус NotFound - ресурс не найден
Отправляя определенный статусный код, мы уже даем клиенту знать о характере возникшей ошибки или статусе запросе.
Но мы не ограничены статусными кодами и, как и в MVC, можем использовать для валидации объект ModelState.
Допустим, мы работаем со следующей моделью:
public class Book { public int Id { get; set; } [Required(ErrorMessage="Укажите название книги")] public string Name { get; set; } [Range(1800, 2000, ErrorMessage = "Год должен быть в промежутке от 1800 до 2000")] [Required(ErrorMessage = "Укажите год издания книги")] public int Year { get; set; } }
В Web API применяются все те же атрибуты валидации, что и в ASP.NET MVC. Общая схема валидации в контроллере Web API, например, при обработке запроса POST, будет выглядеть следующим образом:
public class ValuesController : ApiController { // POST api/values public IHttpActionResult Post(Book book) { // отправка статусного кода 400 if (book == null) return BadRequest(); // обработка частных случаев валидации if (book.Year == 1984) ModelState.AddModelError("book.Year", "Год не должен быть равен 1984"); if (book.Name == "Война и мир") { ModelState.AddModelError("book.Name", "Недопустимое название для книги"); ModelState.AddModelError("book.Name", "Название не должно начинаться с заглавной буквы"); } if (!ModelState.IsValid) return BadRequest(ModelState); // если запрос без ошибок return Ok(); } }
С помощью объекта ModelState здесь валидируется полученная модель book. Но кроме проверки свойства ModelState.IsValid мы также можем добавить и еще дополнительные проверки. Например:
if (book.Year == 1984) ModelState.AddModelError("book.Year", "Год не должен быть равен 1984");
Для добавления дополнительной ошибки используется метод ModelState.AddModelError
, первый параметр которого - ключ ошибки, а второй -
сообщение об ошибке. В качестве ключа мы можем использовать любое значение, но по умолчанию система сохраняет все ошибки свойств модели по ключу
"переменная_модели.Название_свойства". В нашем случае модель преставлена переменной book, поэтому все ошибки, связанные со свойством Year, сохраняются
по ключу book.Year. Причем по одному ключу мы можем указать множество ошибок.
Все ошибки валидаци сохраняются в объекте ModelState, который передается в метод BadRequest
и, таким образом, отправляется клиенту вместе с ошибкой 400.
Теперь рассмотрим, как мы можем получить эти ошибки на стороне клиента. Пусть у нас будет следующее представление:
@{ Layout = null; } <!doctype html> <html> <head> <title>Валидация</title> <meta charset="utf-8" /> </head> <body> <div> <input type="text" name="name" id="nameText" required /> <input type="number" name="year" max="2000" min="1800" id="yearText" required /> <input type="submit" value="Отправить" id="submit" /> </div> <div id="errors" style="color:red;"></div> @Scripts.Render("~/bundles/jquery") <script type="text/javascript"> $(function () { $("#submit").click(function (event) { event.preventDefault(); AddBook(); }); function AddBook() { // получаем значения для добавляемой книги var book = { Name: $('#nameText').val(), Year: $('#yearText').val() }; $.ajax({ url: '/api/values/', type: 'POST', data: JSON.stringify(book), contentType: "application/json;charset=utf-8", success: function (data, status) { $('#errors').empty(); }, error: function (jxqr, error, status) { // парсинг json-объекта var response = jQuery.parseJSON(jxqr.responseText); $('#errors').empty(); $('#errors').append("<h2>" + response['Message'] + "</h2>"); // добавляем ошибки свойства Year if (response['ModelState']['book.Name']){ $.each(response['ModelState']['book.Name'], function (index, item) { $('#errors').append("<p>" + item + "</p>"); }); } // добавляем ошибки свойства Name if (response['ModelState']['book.Year']) { $.each(response['ModelState']['book.Year'], function (index, item) { $('#errors').append("<p>" + item + "</p>"); }); } } }); } }) </script> </body> </html>
Для вывода ошибок здесь определен специальный блок с id="errors". При получении ошибки срабатывает функция error, первый параметр которой хранит
все данные об ошибке, в том числе и посланный объект ModelState. Но чтобы обратиться к ошибкам, надо пройти несколько уровней вложенности.
Например, чтобы получить ошибки свойства Year, придется использовать вызов response['ModelState']['book.Year']
. Получение сообщения об ошибках происходит по тем же ключам,
которые были определены в контроллере. Причем каждый из таких вызовов представляет собой массив.
И теперь если мы введем некорретные данные, мы получим сообщения об ошибках.