Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

.pdf
Скачиваний:
25
Добавлен:
19.03.2016
Размер:
17.66 Mб
Скачать

Рисунок 21-7: Диалоговые окна, которые отображаются в ответ на функцию обратного вызова Ajax

Отображение диалоговых окон пользователю при каждом обратном вызове – не самая полезная вещь, которую можно сделать с обратными вызовами Ajax, но она демонстрирует последовательность, в которой вызываются функции. Мы можем сделать все, что угодно с этими функциями JavaScript: управлять HTML DOM, создавать дополнительные запросы и так далее. Одна из самых полезных вещей, которую мы можем сделать обратным вызовом, это обработка данных JSON. Мы опишем их в следующем разделе.

Работа с JSON

До сих пор в наших Ajax примерах сервер обрабатывал HTML фрагменты и отправлял их браузеру. Это вполне приемлемая техника, но она громоздка (потому что мы посылаем HTML элементы вместе с данными), и она ограничивает то, что мы можем делать с данными в браузере.

Одним из способов решения обеих этих проблем является использование формата JSON (JavaScript Object Notation), который является независимым от языка способом выражения данных. Он возник из языка JavaScript, но давно уже начал свою собственную жизнь и очень широко используется. В этом разделе мы покажем вам, как создать метод действия, который возвращает JSON данные, а также как обрабатывать эти данные в браузере.

Совет

В главе 25 мы описываем Web API, который является альтернативным подходом для создания веб-сервисов.

Добавление поддержки JSON в контроллер

MVC фреймворк делает создание метода действия, который генерирует JSON данные, а не HTML, очень простым. В листинге 21-18 показано, как мы добавили такой метод действия в контроллер

People.

Листинг 21-18: Метод действия, который генерирует данные JSON

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

551

using System.Web.Mvc;

using HelperMethods.Models; namespace HelperMethods.Controllers

{

public class PeopleController : Controller

{

private Person[] personData = {

new Person {FirstName = "Adam", LastName = "Freeman", Role = Role.Admin}, new Person {FirstName = "Steven", LastName = "Sanderson", Role = Role.Admin}, new Person {FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User}, new Person {FirstName = "John", LastName = "Smith", Role = Role.User},

new Person {FirstName = "Anne", LastName = "Jones", Role = Role.Guest} };

public ActionResult Index()

{

return View();

}

private IEnumerable<Person> GetData(string selectedRole)

{

IEnumerable<Person> data = personData; if (selectedRole != "All")

{

Role selected = (Role)Enum.Parse(typeof(Role), selectedRole); data = personData.Where(p => p.Role == selected);

}

return data;

}

public JsonResult GetPeopleDataJson(string selectedRole = "All")

{

IEnumerable<Person> data = GetData(selectedRole); return Json(data, JsonRequestBehavior.AllowGet);

}

public PartialViewResult GetPeopleData(string selectedRole = "All")

{

return PartialView(GetData(selectedRole));

}

public ActionResult GetPeople(string selectedRole = "All")

{

return View((object)selectedRole);

}

}

}

Поскольку мы хотим представить одни и те же данные в двух различных форматах (HTML и JSON), мы переделали наш контроллер так, чтобы был общий (и private) метод GetData, который несет ответственность за выполнение фильтрации.

Мы добавили новый метод действий GetPeopleDataJson, который возвращает объект JsonResult. Это особый вид ActionResult, который говорит движку представления, что мы хотим вернуть клиенту JSON данные, а не HTML. (Вы можете узнать больше о классе ActionResult и роли, которую он играет в MVC фреймворке, в главе 15).

Мы создаем JsonResult при помощи метода Json в методе действия, передавая данные, которые мы хотим преобразовать в формат JSON:

...

return Json(data, JsonRequestBehavior.AllowGet);

...

В данном случае мы также передали в AllowGet значение из перечисления JsonRequestBehavior. По умолчанию данные JSON будут отправлены только в ответ на запрос POST, но, передавая это

552

значение в качестве параметра методу Json, мы говорим MVC фреймворку также реагировать на запросы GET.

Внимание

Вы должны использовать только JsonRequestBehavior.AllowGet, если данные,

которые вы возвращаете, не являются закрытыми. В связи с проблемой безопасности во многих веб браузерах для сторонних сайтов становится возможным перехватывать данные JSON, которые вы возвращаете в ответ на запрос GET, поэтому JsonResult не будет отвечать на запросы GET по умолчанию. Вместо этого в большинстве случаев вы сможете использовать POST запросы для

получения JSON данных, избегая проблемы. Для дополнительной информации см. http://haacked.com/archive/2009/06/25/json-hijacking.aspx.

Обработка JSON в браузере

Для обработки JSON мы возвращаемся к серверу приложений MVC фреймворка, мы определяем функцию JavaScript, используя свойство обратного вызова OnSuccess в классе AjaxOptions. В листинге 21-19 вы можете увидеть, как мы обновили файл представления GetPerson.cshtml, чтобы удалить функции обработчика, которые мы определили в предыдущем разделе, и использовали OnSuccess для обработки данных JSON.

Листинг 21-19: Работа с данными JSON в представлении GetPerson

@using HelperMethods.Models @model string

@{

ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions {

UpdateTargetId = "tableBody",

Url = Url.Action("GetPeopleData"), LoadingElementId = "loading", LoadingElementDuration = 1000,

Confirm = "Do you wish to request new data?" };

}

<script type="text/javascript"> function processData(data) {

var target = $("#tableBody"); target.empty();

for (var i = 0; i < data.length; i++) { var person = data[i];

target.append("<tr><td>" + person.FirstName + "</td><td>"

+ person.LastName + "</td><td>" + person.Role + "</td></tr>");

}

}

</script>

<h2>Get People</h2>

<div id="loading" class="load" style="display: none"> <p>Loading Data...</p>

</div>

<table>

<thead>

<tr>

<th>First</th>

<th>Last</th>

<th>Role</th>

</tr>

</thead>

553

<tbody id="tableBody">

@Html.Action("GetPeopleData", new {selectedRole = Model }) </tbody>

</table>

@using (Ajax.BeginForm(ajaxOpts)) { <div>

@Html.DropDownList("selectedRole", new SelectList( new [] {"All"}.Concat(Enum.GetNames(typeof(Role)))))

<button type="submit">Submit</button> </div>

}

<div>

@foreach (string role in Enum.GetNames(typeof(Role))) { <div class="ajaxLink">

@Ajax.ActionLink(role, "GetPeople", new {selectedRole = role},

new AjaxOptions {

Url = Url.Action("GetPeopleDataJson", new {selectedRole = role}),

OnSuccess = "processData"

})

</div>

}

</div>

Мы определили новую функцию ProcessData, содержащую немного базового JQuery кода, который обрабатывает JSON объекты и использует их для создания элементов tr и td, необходимых для заполнения таблицы.

Совет

Мы не углубляемся в JQuery в данной книге, потому что он сам по себе является обширной темой. Хотя мы любим JQuery, и если вы хотите узнать о нем больше, то обратите внимание на книгу Адама Фримана Pro JQuery (Apress, 2012).

Обратите внимание, что мы удалили значение для свойства UpdateTargetId из объектов AjaxOptions, которые мы создали для ссылок. Если вы забыли это сделать, ненавязчивый Ajax постарается обрабатывать данные JSON, полученные от сервера, как HTML. Обычно это происходит потому, что содержание целевого элемента удаляется, но не заменяется новыми данными.

Вы можете увидеть результат перехода к JSON, запустив приложение, перейдя по URL /People/GetPerson и нажав на одну из ссылок. Как показано на рисунке 21-8, мы получаем не совсем правильный результат: в частности, информация, отображаемая в колонке Role таблицы, не является правильной. Мы объясним, почему это происходит, и покажем вам, как это исправить, в следующем разделе.

Рисунок 21-8: Работа с данными JSON вместо HTML

554

Подготовка данных для кодирования

Когда мы вызвали метод Json внутри метода действия GetPeopleDataJson, мы оставили MVC фреймворк выяснять, как кодировать наши объекты People в формате JSON. MVC фреймворк не имеет специального знания о нашей модели, и поэтому он очень старается предположить, что он должен делать. Вот как MVC фреймворк выражает один объект Person в JSON:

{"PersonId":0,"FirstName":"Adam","LastName":"Freeman",

"BirthDate":"\/Date(62135596800000)\/","HomeAddress":null,"IsApproved":false,"Role":0}

...

Это похоже на кашу, но результат на самом деле довольно разумный: это просто не совсем то, что нам надо. Во-первых, все свойства, определенные классом Person, представлены в JSON, хотя мы и не присваивали им значения в контроллере People. В некоторых случаях используется значение по умолчанию для данного типа (например, false используется для IsApproved), а в других случаях используется null (например, для HomeAddress). Некоторые значения преобразуются так, чтобы их мог легко истолковать JavaScript, такие как свойство BirthDate, а другие обрабатываются не так хорошо, например, использование 0 для свойства Role, а не Admin.

Просмотр данных JSON

Полезно посмотреть, какие JSON данные возвращают методы действий, и самый простой способ сделать это – ввести URL, направленный на действие, в браузер:

http://localhost:57520/People/GetPeopleJson?selectedRole=Admin

Вы можете сделать это фактически в любом браузере, но большинство браузеров заставят вас сохранить и открыть текстовый файл, прежде чем вы сможете увидеть содержимое JSON. Нам нравится использовать для этого браузер Google Chrome, потому что он показывает JSON данные в главном окне браузера, что делает процесс быстрее, и обозначает, что в итоге вы не будете сидеть с десятками открытых окон текстового файла. Мы также рекомендуем Fiddler (www.fiddler2.com), который является отличной прокси веб отладки и позволяет просмотреть всю информацию о данных, передаваемых между браузером и сервером.

MVC фреймворк сделал хорошую попытку, но мы в конечном итоге отправили в браузер свойства, которые мы в дальнейшем не будем использовать, а значение Role не является полезным для нас. Это типичная ситуация, когда перед отправкой данных клиенту требуется немного подготовиться.

В листинге 21-20 можно увидеть, как мы переделали метод действия GetPersonDataJson в контроллере People для подготовки данных, которые мы передаем методу Json.

Листинг 21-20: Подготовка данных для JSON кодировки

...

public JsonResult GetPeopleDataJson(string selectedRole = "All") { var data = GetData(selectedRole).Select(p => new {

FirstName = p.FirstName, LastName = p.LastName,

Role = Enum.GetName(typeof(Role), p.Role) });

return Json(data, JsonRequestBehavior.AllowGet);

}

...

555

Мы использовали LINQ для создания последовательности новых объектов, которые содержат только свойства FirstName и LastName нашего объекта Person и представили значение Role в строковом выражении. В результате этого мы получаем JSON данные, которые содержат только нужные нам свойства, более полезные для нашего jQuery кода:

...

{"FirstName":"Adam","LastName":"Freeman","Role":"Admin"}

...

На рисунке 21-9 показаны измененные выходные данные, отображаемые браузером. Естественно, неиспользованные свойства также отправлены, но вы видите, что столбец Role содержит правильные значения.

Рисунок 21-9: Результат подготовки данных для JSON кодировки

Совет

Возможно, вам нужно очистить историю в браузере, чтобы увидеть изменения.

Обнаружение AJAX запросов в методе действия

Наш контроллер в настоящее время содержит два метода действия, чтобы мы могли поддерживать запросы для HTML и JSON данных. Это, как правило, тот способ, которым мы строим наши контроллеры, потому что нам нравятся много коротких и простых действий, но вы не обязаны работать таким же образом. MVC фреймворк обеспечивает простой способ обнаружения Ajax запросов. Это обозначает, что вы можете создать один метод действия, который управляет разными форматами данных. В листинге 21-21 вы можете увидеть, как мы переделали контроллер Person, чтобы он содержал одно действие, которое обрабатывает как JSON, так и HTML.

556

Листинг 21-21: Создание одного метода действия, который обрабатывает запросы JSON и HTML

using System;

using System.Collections.Generic; using System.Linq;

using System.Web; using System.Web.Mvc;

using HelperMethods.Models; namespace HelperMethods.Controllers

{

public class PeopleController : Controller

{

private Person[] personData = {

new Person {FirstName = "Adam", LastName = "Freeman", Role = Role.Admin}, new Person {FirstName = "Steven", LastName = "Sanderson", Role = Role.Admin}, new Person {FirstName = "Jacqui", LastName = "Griffyth", Role = Role.User}, new Person {FirstName = "John", LastName = "Smith", Role = Role.User},

new Person {FirstName = "Anne", LastName = "Jones", Role = Role.Guest} };

public ActionResult Index()

{

return View();

}

public ActionResult GetPeopleData(string selectedRole = "All")

{

IEnumerable<Person> data = personData; if (selectedRole != "All")

{

Role selected = (Role)Enum.Parse(typeof(Role), selectedRole); data = personData.Where(p => p.Role == selected);

}

if (Request.IsAjaxRequest())

{

var formattedData = data.Select(p => new

{

FirstName = p.FirstName, LastName = p.LastName,

Role = Enum.GetName(typeof(Role), p.Role) });

return Json(formattedData, JsonRequestBehavior.AllowGet);

}

else

{

return PartialView(data);

}

}

public ActionResult GetPeople(string selectedRole = "All")

{

return View((object)selectedRole);

}

}

}

Мы использовали метод Request.IsAjaxRequest для обнаружения Ajax запросов и передачи формата JSON, если результат является true. Есть несколько ограничений, которые вы должны знать прежде, чем будете следовать такому подходу.

Во-первых, метод IsAjaxRequest возвращает true, если браузер включил в свой запрос заголовок X- Requested-With и установил значение на XMLHttpRequest. Это широко используемое соглашение, но оно не является универсальным, и поэтому вы должны рассмотреть тот вариант, будут ли пользователи делать запросы, которые требуют JSON данных, без установки этого заголовка.

557

Вторым ограничением является то, что предполагается, что все AJAX запросы требуют JSON данных. Возможно, ваше приложение будет лучше поддерживаться, если вы разделите способ, которым был сделан запрос, от формата данных, которые ищет клиент. Мы предпочитаем этот подход, и по этой причине мы склонны определять отдельные методы действий для разных форматов данных.

Нам также нужно внести два изменения в наше представление GetPerson.cshtml для поддержки одного метода действия, как показано в листинге 21-22.

Листинг 21-22: Изменение представления GetPerson.cshtml для поддержки одного метода действия

@using HelperMethods.Models @model string

@{

ViewBag.Title = "GetPeople"; AjaxOptions ajaxOpts = new AjaxOptions {

Url = Url.Action("GetPeopleData"), LoadingElementId = "loading", LoadingElementDuration = 1000,

OnSuccess = "processData"

};

}

<script type="text/javascript"> function processData(data) {

var target = $("#tableBody"); target.empty();

for (var i = 0; i < data.length; i++) { var person = data[i];

target.append("<tr><td>" + person.FirstName + "</td><td>"

+person.LastName + "</td><td>" + person.Role

+"</td></tr>");

}

}

</script>

<h2>Get People</h2>

<div id="loading" class="load" style="display: none"> <p>Loading Data...</p>

</div>

<table>

<thead>

<tr>

<th>First</th>

<th>Last</th>

<th>Role</th>

</tr>

</thead>

<tbody id="tableBody">

@Html.Action("GetPeopleData", new {selectedRole = Model }) </tbody>

</table>

@using (Ajax.BeginForm(ajaxOpts)) { <div>

@Html.DropDownList("selectedRole", new SelectList( new [] {"All"}.Concat(Enum.GetNames(typeof(Role)))))

<button type="submit">Submit</button> </div>

}

<div>

@foreach (string role in Enum.GetNames(typeof(Role))) { <div class="ajaxLink">

@Ajax.ActionLink(role, "GetPeople", new {selectedRole = role},

558

new AjaxOptions {

Url = Url.Action("GetPeopleData", new {selectedRole = role}), OnSuccess = "processData"

})

</div>

}

</div>

Первое изменение заключается в объекте AjaxOptions, который мы используем для Ajax формы. Поскольку мы уже не можем получить фрагмент HTML через Ajax запрос, мы должны использовать ту же функцию ProcessData для обработки JSON данных ответа сервера, которую мы создали для Ajax ссылок. Второе изменение заключается в значении свойства Url объектов AjaxOptions, которые мы создали для ссылок. Действия GetPeopleDataJson больше не существует, вместо этого и мы нацелены на действие GetPeopleData.

Резюме

В этой главе мы рассмотрели ненавязчивый Ajax в MVC, который позволяет нам воспользоваться функциональностью библиотеки JQuery простым и элегантным способом без добавления большого количества кода в представления. Если вы хотите работать с фрагментами HTML, то вам вообще не нужно добавлять какой-либо JavaScript код в представления. Но нам нравится работать с JSON, и это означает, что мы, как правило, работаем с функциями JavaScript, которые используют JQuery для обработки данных и создания нужных HTML элементов. В следующей главе мы рассмотрим один из самых интересных и полезных аспектов MVC фреймворка: модель связывания данных.

559

Связывание данных модели

Связывание данных – это процесс создания объектов .NET с помощью данных, отправляемых браузером в запросе HTTP. Мы полагались на связывание данных каждый раз, когда определяли метод действия, который принимает параметр - объекты параметра создаются с помощью связывания данных. В этой главе мы продемонстрируем, как работает система связывания данных, и рассмотрим способы для ее продвинутой настройки.

Подготовка проекта для примера

Для этой главы мы создали в Visual Studio новый проект MVC под названием MvcModels на шаблоне Basic. Мы будем использовать тот же класс модели, что и в предыдущих главах, поэтому создайте новый файл под названием Person.cs в папке Models и приведите его содержимое в соответствие с листингом 22-1.

Листинг 22-1: Файл /Models/Person.cs

using System;

namespace MvcModels.Models

{

public class Person

{

public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } public Address HomeAddress { get; set; } public bool IsApproved { get; set; } public Role Role { get; set; }

}

public class Address

{

public string Line1 { get; set; } public string Line2 { get; set; } public string City { get; set; } public string PostalCode { get; set; } public string Country { get; set; }

}

public enum Role

{

Admin,

User,

Guest

}

}

Мы также создали контроллер Home, как показано в листинге 22-2. В этом контроллере определено для примера несколько объектов Person, а также действие Index, которое позволяет выбрать один объект Person по значению свойства PersonId.

Листинг 22-2: Контроллер Home

using System;

using System.Collections.Generic; using System.Linq;

using System.Web;

560

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]