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

ASP_NET_MVC_4_Framework_s_primerami_na_C_dlya_p

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

Листинг 19-5: Содержимое файла CustomerHelpers.cs

using System.Web.Mvc;

namespace HelperMethods.Infrastructure

{

public static class CustomHelpers

{

public static MvcHtmlString ListArrayItems(this HtmlHelper html, string[] list)

{

TagBuilder tag = new TagBuilder("ul"); foreach (string str in list)

{

TagBuilder itemTag = new TagBuilder("li"); itemTag.SetInnerText(str);

tag.InnerHtml += itemTag.ToString();

}

return new MvcHtmlString(tag.ToString());

}

}

}

Данный вспомогательный метод выполняет ту же функцию, что и встроенный вспомогательный метод в предыдущем примере - он принимает массив строк и генерирует HTML-элемент ul, содержащий элемент li для каждой строки в массиве.

Первый параметр этого вспомогательного метода – объект HtmlHelper с ключевым словом this, которое сообщает компилятору C#, что мы определяем метод расширения. Свойства HtmlHelper обеспечивают доступ к информации, которая может быть полезна при создании контента; они описаны в таблице 19-1.

Таблица 19-1: Свойства класса HtmlHelper

Свойство

 

Описание

RouteCollectionВозвращает набор маршрутов, определенных в приложении.

Возвращает данные объекта из ViewBag, который был передан методом действия в

ViewBag

представление, вызвавшее данный вспомогательный метод.

Возвращает объект ViewContext, который обеспечивает доступ к информации о

ViewContext

запросе и процессе его обработки (и который мы опишем далее в этой главе).

Свойство ViewContext наиболее полезно для создания контента, который адаптируется к текущему запросу. В таблице 19-2 описаны наиболее часто используемые свойства, определенные классом

ViewContext.

Таблица 19-2: Свойства класса ViewContext

Свойство

 

Описание

 

 

 

Controller

 

Возвращает контроллер, обрабатывающий текущий запрос.

 

 

 

HttpContext

 

Возвращает объект HttpContext для текущего запроса.

 

 

 

IsChildAction

 

Возвращает true, если вызвавшее вспомогательный метод представление

 

визуализируется дочерним действием (дочерние действия рассмотрены в главе 18).

 

 

 

 

 

RouteData

 

Возвращает данные маршрутизации для запроса.

 

 

 

View

 

Возвращает экземпляр реализации IView, которая вызвала вспомогательный метод.

 

481

С помощью вспомогательных методов вы можете получить всеобъемлющую информацию о запросе, но по большей части они используются, чтобы сохранить последовательность форматирования. Чтобы генерировать контент для определенных запросов, можно использовать встроенные (built-in) вспомогательные методы (они будут описаны далее в этой главе) или с помощью частичных представлений и дочерних действий реализовывать более сложные решения.

В нашем примере вспомогательного метода не требуется никакой информации о запросе, но нужно создать несколько элементов HTML. Самый простой способ создать код HTML во вспомогательных методах - это использовать класс TagBuilder, который позволяет создавать строки HTML, не используя специальные символы. Класс TagBuilder является частью сборки System.Web.WebPages.Mvc, но так как в нем используется функция миграции типов (type forwarding), кажется, что он является частью сборки System.Web.Mvc. Visual Studio добавляет в проекты MVC обе сборки, и использовать класс TagBuilder достаточно легко, хотя его и нет в документации Microsoft Developer Network (MSDN) API.

Чтобы создать новый экземпляр TagBuilder, передайте в конструктор как параметр имя элемента HTML, который хотите создать. В классе TagBuilder не нужно использовать угловые скобки (< и >), то есть создать элемент ul можно следующим образом:

TagBuilder tag = new TagBuilder("ul");

Наиболее часто используемые члены класса TagBuilder описаны в таблице 19-3.

Таблица 19-3: Члены класса TagBuilder

Член

Innerhtml

SetInnerText(string)

AddCssClass(string)

MergeAttribute(string, string, bool)

Описание

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

Устанавливает текстовое содержимое элемента HTML. Параметр string кодируется для безопасности отображения (см. раздел о кодировании строк HTML ранее в этой главе).

Добавляет класс CSS к элементу HTML.

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

Результатом вспомогательного метода HTML является объект MvcHtmlString, содержимое которого записывается непосредственно в ответ клиенту. В нашем примере мы передаем результат метода TagBuilder.ToString в конструктор объекта MvcHtmlString, как показано здесь:

return new MvcHtmlString(tag.ToString());

Этот оператор генерирует фрагмент кода HTML, который содержит элементы ul и li, и возвращает их в движок представлений, чтобы далее они были записаны в ответ.

Используем пользовательский внешний вспомогательный метод

Применение пользовательских внешних вспомогательных методов немного отличается от внутренних. В листинге 19-6 показаны изменения, которые мы внесли в файл /Views/Home/Index.cshtml для замены внутреннего вспомогательного метода на внешний.

482

Листинг 19-6: Используем пользовательский внешний вспомогательный метод в файле

Index.cshtml

@model string

@using HelperMethods.Infrastructure

@{

Layout = null;

}

<!DOCTYPE html> <html>

<head>

<meta name="viewport" content="width=device-width" /> <title>Index</title>

</head>

<body>

<div>

Here are the fruits: @Html.ListArrayItems((string[])ViewBag.Fruits)

</div>

<div>

Here are the cities: @Html.ListArrayItems((string[])ViewBag.Cities)

</div>

<div>

Here is the message: <p>@Model</p>

</div>

</body>

</html>

Убедитесь, что в файле есть пространство имен, содержащее вспомогательный метод. Чтобы добавить его, мы использовали тег @using, но если вы разрабатываете много пользовательских вспомогательных методов, то можно добавить данное пространство имен в файл /Views/Web.config, чтобы они были доступны во всех представлениях.

Мы ссылаемся на вспомогательный метод с помощью @Html.<helper>, где <helper> - это имя метода расширения, в данном случае Html.ListArrayItems. Часть Html в этом выражении ссылается на свойство, которое определено в базовом классе представления и возвращает объект HtmlHelper, то есть тип, к которому мы применили метод расширения в листинге 19-5.

Мы передаем данные во внешний вспомогательный метод таким же образом, как и во внутренний или любой другой метод C#. Нужно только привести динамических свойства объекта ViewBag к типу, определенному этим внешним методом - в данном случае, к массиву строк. Пусть это и не такой красивый синтаксис, как во внутренних вспомогательных методах, но только так можно создать удобный многоразовый вспомогательный метод.

Когда использовать вспомогательные методы

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

Мы используем вспомогательные методы только для того, чтобы уменьшить количество дублированного кода в представлениях, и только для самого простого кода, как в приведенном примере. Для более сложной разметки и кода мы используем частичные представления, а если необходимо выполнить какие-либо манипуляции с моделью данных - дочерние действия. Мы советуем вам придерживаться такой же схемы и использовать вспомогательные методы только для самых простых решений (в тех случаях, когда количество операторов C# будет бОльшим или даже превысит количество элементов HTML, мы будем переключаться на дочерние действия).

483

Управляем кодировкой строк в вспомогательных методах

В MVC Framework для защиты от вредоносного ввода применяется автоматическое кодирование, которое позволяет гарантировать безопасность добавления данных на страницу. Пример кодирования вы можете увидеть в листинге 19-7, где мы передаем потенциально небезопасную строку в представление в качестве модели объекта (в листинге повторяется код контроллера Home).

Листинг 19-7: Контроллер Home

using System.Web.Mvc;

namespace HelperMethods.Controllers

{

public class HomeController : Controller

{

public ActionResult Index()

{

ViewBag.Fruits = new string[] {"Apple", "Orange", "Pear"}; ViewBag.Cities = new string[] {"New York", "London", "Paris"};

string message = "This is an HTML element: <input>";

return View((object) message);

}

}

}

Объект модели содержит допустимый элемент HTML, но после обработки Razor мы видим следующий код:

<div>

Here is the message:

<p>This is an HTML element: <input></p> </div>

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

Описание проблемы

Чтобы дать вам представление о проблеме, связанной с кодировкой строк, мы создали новый вспомогательный метод в классе CustomerHelpers, как показано в листинге 19-8. Этот вспомогательный метод принимает в качестве параметра строку и генерирует такой же HTML, который мы включили в представление Index.

Листинг 19-8: Определяем новый вспомогательный метод

using System.Web.Mvc; using System;

namespace HelperMethods.Infrastructure

{

public static class CustomHelpers

{

484

public static MvcHtmlString ListArrayItems(this HtmlHelper html, string[] list)

{

TagBuilder tag = new TagBuilder("ul"); foreach (string str in list)

{

TagBuilder itemTag = new TagBuilder("li"); itemTag.SetInnerText(str);

tag.InnerHtml += itemTag.ToString();

}

return new MvcHtmlString(tag.ToString());

}

public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg)

{

string result = String.Format("This is the message: <p>{0}</p>", msg); return new MvcHtmlString(result);

}

}

}

Мы используем метод String.Format, чтобы создать разметку HTML и передать результат в качестве аргумента в конструктор MvcHtmlString. В листинге 19-9 показаны изменения в представлении /View/Home/Index.cshtml, с помощью которых мы будем использовать новый вспомогательный метод (а также выделим контент, который генерируется вспомогательным методом).

Листинг 19-9: Используем вспомогательный метод DisplayMessage в представлении Index

@model string

@using HelperMethods.Infrastructure

@{

Layout = null;

}

<!DOCTYPE html> <html>

<head>

<meta name="viewport" content="width=device-width" /> <title>Index</title>

</head>

<body>

<p>This is the content from the view:</p>

<div style="border: thin solid black; padding: 10px"> Here is the message:

<p>@Model</p>

</div>

<p>This is the content from the helper method:</p> <div style="border: thin solid black; padding: 10px">

@Html.DisplayMessage(Model)

</div>

</body>

</html>

Вы можете увидеть результат применения вспомогательного метода, запустив приложение, как показано на рисунке 19-3.

Рисунок 19-3: Сравниваем кодировку значений данных

485

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

Кодируем контент, генерируемый вспомогательными методами

Есть несколько способов решения этой проблемы, и выбор между ними зависит от характера контента, который генерируется вспомогательным методом.

Самое простое решение - изменить тип вывода вспомогательного метода на string, как показано в листинге 19-10. Это сообщает движку представления, что контент не является безопасным и должен быть закодирован, прежде чем он будет добавлен в представление.

Листинг 19-10: Razor кодирует контент, созданный вспомогательным методом

using System.Web.Mvc; using System;

namespace HelperMethods.Infrastructure

{

public static class CustomHelpers

{

public static MvcHtmlString ListArrayItems(this HtmlHelper html, string[] list)

{

486

TagBuilder tag = new TagBuilder("ul"); foreach (string str in list)

{

TagBuilder itemTag = new TagBuilder("li"); itemTag.SetInnerText(str);

tag.InnerHtml += itemTag.ToString();

}

return new MvcHtmlString(tag.ToString());

}

public static string DisplayMessage(this HtmlHelper html, string msg)

{

return String.Format("This is the message: <p>{0}</p>", msg);

}

}

}

Таким образом, Razor будет кодировать весь контент, который возвращается вспомогательным методом. В целом это очень удобно, за исключением тех случаев, когда необходимо создать элементы HTML, как в данном примере. Результат показан на рисунке 19-4.

Рисунок 19-4: Движок представлений кодирует ответ вспомогательного метода

Мы решили проблему с элементом input, но наши элементы p также были закодированы, и мы получили не совсем то, что хотели. В таких ситуациях нужно быть более избирательными и кодировать только значения данных. Это показано в листинге 19-11.

487

Листинг 19-11: Применяем выборочное кодирование данных во вспомогательном методе

public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg)

{

string encodedMessage = html.Encode(msg); string result = String.Format(

"This is the message: <p>{0}</p>", encodedMessage); return new MvcHtmlString(result);

}

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

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

Рисунок 19-5: Эффект выборочного кодирования контента во внешнем вспомогательном методе

488

Использование встроенных вспомогательных методов

MVC Framework включает в себя несколько встроенных вспомогательных методов, предназначенных для создания форм HTML. В следующих разделах мы рассмотрим эти вспомогательные методы в действии и научимся их использовать.

Создаем элементы form

Одним из наиболее распространенных способов взаимодействия с пользователем в веб-приложении являются HTML-формы, которые можно создавать с помощью целого ряда вспомогательных методов. Чтобы продемонстрировать методы, предназначенные для работы с формами, мы внесли некоторые дополнения в наш пример проекта. Для начала мы создали новый класс Person.cs в папке Models. Содержимое этого файла показано в листинге 19-12 – тип Person будет нашей моделью представления, с помощью которой мы продемонстрируем связанные с формами вспомогательные методы, а типы Address и Role помогут нам продемонстрировать некоторые более сложные функции.

Листинг 19-12: Модель Person

using System;

namespace HelperMethods.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, в которых будут использоваться объекты модели и которые показаны в листинге 19-13.

489

Листинг 19-13: Добавляем методы действий в контроллер Home

using System.Web.Mvc; using HelperMethods.Models;

namespace HelperMethods.Controllers

{

public class HomeController : Controller

{

public ActionResult Index()

{

ViewBag.Fruits = new string[] {"Apple", "Orange", "Pear"}; ViewBag.Cities = new string[] {"New York", "London", "Paris"}; string message = "This is an HTML element: <input>";

return View((object) message);

}

public ActionResult CreatePerson()

{

return View(new Person());

}

[HttpPost]

public ActionResult CreatePerson(Person person)

{

return View(person);

}

}

}

Это стандартный подход к работе с HTML-формами, который включает два метода. Здесь мы полагаемся на механизм связывания данных, предполагая, что MVC Framework создаст объект Person из данных формы и передаст его в метод действия с помощью атрибута HttpPost. (Атрибут HttpPost рассматривается в главе 16, а связывание данных - в главе 22).

Мы никак не обрабатываем данные формы, потому что хотим сосредоточиться на том, как генерировать элементы в представлении. Наш метод действия HttpPost просто вызывает метод View и передает в него объект Person, который он получил в качестве параметра; таким образом мы отображаем пользователю введенные им в форму данные.

Для начала мы создадим стандартную HTML-форму вручную, а затем покажем вам, как заменить различные ее части с помощью вспомогательных методов. Первоначальную версию формы вы можете увидеть в листинге 19-14; в нем показано содержимое представления CreatePerson.cshtml, которое мы создали в папке /Views/Home.

Листинг 19-14: Первоначальная версия формы HTML

@model HelperMethods.Models.Person

@{

ViewBag.Title = "CreatePerson";

}

<h2>CreatePerson</h2>

<form action="/Home/CreatePerson" method="post"> <div class="dataElem">

<label>PersonId</label>

<input name="personId" value="@Model.PersonId"/> </div>

<div class="dataElem">

490

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