Обработка ошибок привязки модели
69ASP.NET --- ASP.NET Web Forms 4.5 --- Обработка ошибок привязки модели
В предыдущей статье мы настроили привязку модели и ввели ряд средств проверки достоверности, но мы еще не выражаем достаточно хорошо ошибки для пользователя, а просто генерируем исключение и позволяем стандартным средствам обработки ошибок отображать сообщение. К счастью, средство привязки моделей обладает возможностями по улучшению данной ситуации. Цель заключается в том, чтобы вывести пользователю сообщения обо всех встреченных ошибках привязки модели и проверки достоверности за один раз, чтобы он мог внести необходимые коррективы перед тем, как отправлять форму заново. Это намного лучше, чем сообщать о проблемах по очереди, что может довольно сильно утомить пользователя.
Для достижения этой цели мы внесли ряд изменений в файл веб-формы Default.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Binding.Default" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title>Привязка моделей</title>
<style>
...
</style>
</head>
<body>
<asp:PlaceHolder ID="ErrorPanel" Visible="false" runat="server">
<div class="error panel">
Исправьте следующие ошибки:
<ul>
<asp:Repeater ID="Repeater1" SelectMethod="GetModelValidationErrors"
ViewStateMode="Disabled" ItemType="System.String" runat="server">
<ItemTemplate>
<li><%# Item %></li>
</ItemTemplate>
</asp:Repeater>
</ul>
</div>
</asp:PlaceHolder>
<div class="panel">
...
</div>
<div class="panel">
...
</div>
</body>
</html>
С помощью элемента управления Repeater ошибки отображаются в виде элементов списка; сведения об ошибках получаются из метода GetModelValidationErrors() класса отделенного кода. Мы также добавили литеральный контент, который наряду с Repeater содержится в элементе управления PlaceHolder. Элемент управления Repeater будет генерировать по одному элементу <li> для каждой ошибки, возвращаемой методом GetModelValidationErrors() класса отделенного кода, а контент из Repeater и литеральный контент будут включены в ответ, только если свойство или атрибут Visible для элемента управления PlaceHolder установлен в true.
В примере ниже показаны изменения, внесенные в файл отделенного кода Default.aspx.cs, где производится манипулирование элементом управления PlaceHolder и предоставление данных для Repeater:
// ...
using System.Collections.Generic;
namespace Binding
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
DisplayUser(GetUser());
ErrorPanel.Visible = !ModelState.IsValid;
}
protected User GetUser()
{
User model = new User();
IValueProvider provider = new FormValueProvider(ModelBindingExecutionContext);
TryUpdateModel<User>(model, provider);
return model;
}
protected void DisplayUser(User user)
{
// ...
}
public IEnumerable<string> GetModelValidationErrors()
{
if (!ModelState.IsValid)
foreach (KeyValuePair<string, ModelState> pair in ModelState)
foreach (ModelError error in pair.Value.Errors)
if (!String.IsNullOrEmpty(error.ErrorMessage))
yield return error.ErrorMessage;
}
}
}
Метод GetUser() модифицирован так, чтобы не генерировать исключение, когда метод TryUpdateModel<T>() возвращает false, как мы поступали с ошибками ранее. Мы добавили метод GetModelValidationErrors(), в котором реализован новый прием обработки ошибок. Свойство Page.ModelState возвращает экземпляр класса ModelStateDictionary, содержащего информацию о выполненной привязке модели и проверке достоверности, в котором определены свойства, описанные в таблице ниже:
Имя | Описание |
---|---|
IsValid | Возвращает true, если ошибки привязки модели отсутствуют, и false, если они есть |
Keys | Возвращает коллекцию ключей, содержащихся в словаре |
Values | Возвращает коллекцию значений, содержащихся в словаре |
Свойство IsValid используется для контроля видимости элемента управления PlaceHolder, т.е. литеральный контент и результат из элемента управления Repeater будут включаться в ответ только при наличии ошибок. Класс ModelStateDictionary реализует интерфейс IEnumerable<KeyValuePair<string, ModelState>>, который позволяет пройти по очереди по всем свойствам, привязанным к модели. Часть string в KeyValuePair - это имя свойства модели, которое описано посредством части ModelState. В классе ModelState определены свойства, описанные ниже:
- Errors
Возвращает коллекцию объектов ModelError, которые описывают ошибки, возникшие во время привязки предоставленного значения к свойству модели.
- Value
Возвращает значение, которое использовалось в привязке модели.
Класс ModelError описывает одиночную ошибку привязки модели и определяет свойства, перечисленные ниже:
- ErrorMessage
Получает сообщение об ошибке, выдаваемое атрибутом проверки достоверности
- Exception
Возвращает исключение, вызванное ошибкой проверки достоверности
Может показаться, что в описание ошибок вовлечено слишком много объектов, но на самом деле с ними легко работать, и они позволяют системе привязки моделей сообщать об ошибках для множества свойств. Это значит, что мы можем предоставлять пользователю единый список всех ошибок проверки достоверности, которые возникли.
В методе GetModelValidationErrors() используется тот факт, что объект ModelStateDictionary выдает объекты KeyValuePair<string, ModelState>, когда применяется в цикле foreach для обработки всей информации, доступной о процессе привязки модели. Для каждой пары "ключ/значение" мы используем объект Value, чтобы получить ModelState и пройти по содержащейся в ней коллекции объектов ModelError, выдавая последовательность значений ErrorMessage.
Это довольно трудный для понимания код, поэтому не переживайте, если не поняли его - можете воспользоваться кодом из примера в качестве шаблона, а позже мы покажем более простой прием, основанный на применении встроенного элемента управления. В конечном итоге элементу управления Repeater передается последовательность ошибок в случае их возникновения. Это более элегантный подход к отображению ошибок для пользователя, который предоставляет возможность внести сразу несколько исправлений перед отправкой данных на повторную проверку достоверности:
Это определенное улучшение по сравнению с предыдущим подходом, но для выполнения проверки достоверности данных пользователь по-прежнему должен отправлять форму серверу.
Использование сводки по проверке достоверности
Мы продемонстрировали способ получения и отображения ошибок проверки достоверности вручную, поскольку это помогает понять взаимодействие между разными частями ASP.NET Framework. В состав ASP.NET входит встроенный элемент управления под названием ValidationSummary, который отображает ошибки, но не генерирует вывод, если ошибки отсутствуют.
В примере ниже приведено содержимое файла Default.aspx с добавленным элементом управления ValidationSummary:
...
<body>
<form id="form1" runat="server">
<asp:ValidationSummary HeaderText="Исправьте следующие ошибки:" CssClass="error" runat="server" />
<div class="panel">
...
</div>
<div class="panel">
...
</div>
</form>
</body>
</html>
Обратите внимание на небольшую реструктуризацию веб-формы - причина в том, что элемент управления ValidationSummary должен использоваться внутри элемента <form> серверной стороны. (Это еще одна причина, по которой мы объясняем ручной подход: элемент управления ValidationSummary неприменим в случае работы с несколькими формами.) В элементе управления ValidationSummary определены следующие свойства:
- DisplayMode
Указывает, каким образом элементы отображаются. Допустимыми значениями являются BulletList (стандартное значение, обеспечивающее генерацию элементов <ul> и <li>), List (разделяет ошибки с помощью элементов <br>) и SingleParagraph (помещает все сообщения об ошибках в единственный литеральный текстовый блок).
- HeaderText
Указывает строку, отображаемую перед сообщениями об ошибках
В примере выше было принято стандартное значение для атрибута DisplayMode (т.е. ошибки будут отображаться в виде элементов списка) и указана строка в атрибуте HeaderText, которая отобразится перед списком ошибок. Мы также установили свойство CssClass, которое назначает HTML-элементу верхнего уровня, сгенерированному элементом управления ValidationSummary, класс error, в результате чего сообщение об ошибке отображается с использованием того же стиля, что и у списка, построенного вручную.
В элементе управления ValidationSummary определены и другие свойства, поддерживающие подход к проверке достоверности, который применялся до выхода версии ASP.NET 4.5 и требовал помещения специальных элементов управления проверкой рядом с каждым полем формы. Мы не описывали эти свойства, поскольку появившаяся в ASP.NET 4.5 поддержка привязки моделей и проверки достоверности проще как в работе, так и в сопровождении.
Элемент управления ValidationSummary обнаруживает ошибки привязки модели и проверки достоверности автоматически и отображается только при наличии ошибок, которые должны быть показаны пользователю. И это означает, что мы можем упростить класс отделенного кода, т.к. больше не придется предоставлять элементу управления Repeater сообщения об ошибках и контролировать видимость этого элемента управления.
Упрощенный класс отделенного кода представлен в примере ниже:
using System;
using Binding.Models;
using System.Text.RegularExpressions;
using System.Web.ModelBinding;
using System.Collections.Generic;
namespace Binding
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
DisplayUser(GetUser());
}
protected User GetUser()
{
User model = new User();
IValueProvider provider = new FormValueProvider(ModelBindingExecutionContext);
TryUpdateModel<User>(model, provider);
return model;
}
protected void DisplayUser(User user)
{
sname.InnerText = user.Name;
sage.InnerText = user.Age.ToString();
scell.InnerText = user.Cell;
szip.InnerText = user.Zip;
}
}
}
Никаких визуальных изменений, связанных с использованием элемента управления ValidationSummary, не происходит, поскольку он отображает ошибки таким же способом, как мы делали вручную. В этом легко убедиться, запустив приложение и отправив форму со значениями, которые не пройдут проверку или не могут быть преобразованы в типы, указанные для свойств класса модели: