1. Введение
Типичной задачей Web-приложения является представление статической информации на страницах, т. е. информации, которую пользователи не могут изменить. К этим Web-приложениям относятся, главным образом, Web-приложения, публикующие новости, например http://gazeta.ru, http://www.news.com.au. Проблема, касающаяся этой информации, в следующем: она может содержать в себе символы, которые браузеры неправильно отображают, например, при вводе текста элемента управления Label:
lbMsg.Text = "if a<b then";
lbMsg.Text = "для полужирного текста следует использовать <b> тег";
в окне браузера он отображается как:
if a
для полужирного текста следует использовать тег
Видно, что тексты отображаются неверно, т.к. в них содержатся символы “<” и “>”, интерпретируемые браузером как часть HTML-разметки страницы. Для решения указанной проблемы необходимо кодировать текстовые элементы в HTML-эквиваленты, например, “<” кодируется в “<”. В таблице перечислены специальные символы и их эквиваленты [1].
Таблица
HTML-эквиваленты специальных символов
Символы |
Описание |
Html-эквиваленты |
|
Пробел |
|
< |
Меньше чем |
< |
> |
Больше чем |
> |
& |
Амперсанд |
& |
“ |
Кавычки |
" |
Таким образом, необходимо реализовать функциональность кодирования гипертекста при задании текста для элементов управления вида Label, LinkButton, HyperLink, CheckBox, ListItem, RadioButton.
В данной статье рассмотрим, как реализовать HTML-кодирование текста в ASP.NET-приложении с помощью аспектно-ориентированного программирования (АОП) в системе Aspect.NET [2, 3], разработанной в лаборатории Java-технологии математико-механического факультета СПбГУ под руководством профессора В.О. Сафонова.
2. HTML-Кодирование текста
В ASP.NET-приложении используется метод HttpServerUtility.HtmlEncode() для кодирования обычного текста в текст с допустимыми HTML-символами [1].
lbMsg.Text = Server.HtmlEncode("if a<b then");
lbMsg.Text = Server.HtmlEncode("для полужирного текста следует использовать <b> тег");
Server в данном коде – это свойство класса System.Web.UI.Page, которое имеет тип HtmlServerUtility. Тогда на браузере тексты будут отображены правильно:
if a<b then
для полужирного текста следует использовать <b> тег
И кодированные тексты будут:
if a<b then
для полужирного текста следует использовать <b> тег
Для декодирования используется метод HttpServerUtility.HtmlDecode().
string text = Server.HtmlDecode(lbMsg.Text);
Метод HtmlEncode()часто используется в случаях, когда мы извлекаем текст из базы данных и не знаем, что допустится ли текст HTML.
Одна проблема, связанная с методом HtmlEncode(), – он не кодирует последовательность пробелов в последовательность “ ”. Т.е если наш текст содержит последовательность пробелов, то браузер будет отображать только один пробел. Для решения этой проблемы можем поменять пробелы на “ ” вызовом метода String.Replace():
string text = Server.HtmlEncode(sometext);
text.Replace(" ", " ");
Кроме этого, HtmlEncode() не преобразует разрыв строки в “<br/>”, поэтому при необходимости надо добавить код для этого преобразования.
3. Применение АОП для HTML-кодирования текста
Обсуждение в предыдущем разделе показывает, что, HTML-кодирование очень легко выполнить вызовом метода HttpServerUtility.HtmlEncode(). Но всякий раз, когда нам необходимо кодировать текст, следует вызвать этот метод и для декодирования следует вызвать метод HttpServerUtility.HtmlDecode(). Т.е. необходимо вызвать многократно эти методы в многих точках выполнения Web-приложения. Кроме вызова этих методов, в многих случаях нам необходимо написать дополнительный код для исправления их недостатки (например необходимо вызвать text.Replace(" ", " "); или написать код для преобразования разрыва строки в “<br/>”). Либо, после внедрения Web-приложения, информация отображена на страницах неправильно из-за того, что данные в базе данных содержат специальные символы, описанные в разделе 1. При этом, разработчикам приходится добавить новую функциональность HTML-кодирование, применяя подходы выше, в фиксированных точках выполнения Web-приложения. Можно использовать сило ООП для реализации изменения. Т.е. создается какой-то модуль для реализации кодирования, но при этом в каждые из этих точек приходится изменить код для вызова этого модуля. Данная проблема решена аспектно-ориентированным программированием (АОП), т.к. видно что кодирование и декодирование есть две сквозной функциональности (cross-cutting concers) – функциональность, реализация которой рассредоточена по коду приложения. С помощью АОП каждая функциональность реализуется в аспекте в виде набора действий (actions), затем определяются условия внедрения для присоединения этих действий к нужным нам точкам выполнения Web-приложения, после этого запускается подсистема внедрения (weaver) аспектов системы Aspect.NET. Действия аспекта будут автоматически добавляться подсистемой внедрения в точки присоединения (т. е. в нужные нам точки выполнения), определенные условиями внедрения аспекта. При этом изменения целевого Web-приложения выполняются на уровне MSIL кода.
4. Аспект HTML-кодирования
Для практического подтверждения описанной идеи было принято решение разработать аспект, поддерживающий HTML-кодирование текста, с использованием системы Aspect.NET.
Допустим, в Web-приложении существует точка выполнения, в которой необходимо установить текст для элемента управления Label:
lbArticleBody.Text = strTextIsDB;
strTextIsDB – текстовое значение, которое может содержать в себе символы, некорректно отображаемые браузерами, и другая текстовая информация после него тоже некорректно отображается. Следовательно, существует небоходимость его кодирования для исправления данной ситуации.
Создадим аспект с именем HtmlEncodingAspect. Добавим в него действие HtmlEncodeAdvice(), которое будет внедрено в указанной точке выполнения:
[AspectAction("%instead %call Label.set_Text(string) && %args(arg[0])")]
public static void HtmlEncodeAdvice(string text)
{
Label label = (Label)TargetObject;
string encodedText =
HttpContext.Current.Server.HtmlEncode(text);
encodedText = encodedText.Replace(" ", " ");
label.Text = encodedText;
}
В данном действии после вызова метода HtmlEncode() добавлен код для преобразования последовательности пробелов в последовательность “ ”. После этого получим закодированный текст для элемента управления. Условие внедрения аспекта "%instead %call Label.set_Text(string) && %args(arg[0])" означает, что вместо вызова метода установки текста для элемента управления Label (Label.set_Text()) вызывается действие аспекта HtmlEncodeAdvice(string text). Можно присоединить это действие к точкам выполнения, где текст установлен для других элементов управления (например, LinkButton, HyperLink, CheckBox, ListItem, RadioButton). При этом необходимо изменить условие внедрения так, чтобы оно соответствовало данным точкам выполнения (синтаксис условия внедрения описан в [3]), например, "%instead %call *.set_Text(string) && %args(arg[0])", т. е. при вызове метода установки текста объекта любого класса. В теле действия HtmlEncodeAdvice() необходимо проверить тип целевого объекта (TargetObject), например:
[AspectAction("%instead %call *.set_Text(string) && %args(arg[0])")]
public static void HtmlEncodeAdvice(string text)
{
string encodedText = HttpContext.Current.Server.HtmlEncode(text);
encodedText = encodedText.Replace(" ", " ");
if (TargetObject is Label)
{
((Label)TargetObject).Text = encodedText;
}
else if (TargetObject is CheckBox)
{
((CheckBox)TargetObject).Text = encodedText;
}
}
Рассмотрим, как реализуется действие аспекта для декодирования. Добавим в аспект HtmlEncodingAspect следующее действие:
[AspectAction("%instead %call Label.get_Text()")]
public static string HtmlDecodeAdvice()
{
Label label = (Label)TargetObject;
string encodedText = label.Text;
encodedText = encodedText.Replace(" ", " ");
return HttpContext.Current.Server.HtmlDecode(encodedText);
}
Условие внедрения аспекта "%instead %call Label.get_Text()" означает, что вместо вызова метода Label.get_Text(), вызывается действие аспекта HtmlDecodeAdvice(). Кроме того, можно написать условие внедрения для вызова метода get_Text() объекта класса LinkButton, HyperLink, CheckBox, ListItem, RadioButton и т. д.
Применение АОП в системе Aspect.NET для реализации HTML-кодирования текста имеет следующие преимущества по сравнению с его реализацией без применения АОП:
- Использование синтаксиса определения условия внедрения аспекта [2] позволяет описать множество точек присоединения, в которые необходимо добавить кодирование и декодирование текста.
- Действия аспекта будет автоматически добавляться подсистемой внедрения (weaver) в точки присоединения (т. е. в нужные нам точки выполнения), определенные условиями внедрения аспекта. Таким образом, не требуется “ручных” вставок кода реализации кодирования и декодирования текста в этих точках выполнения. Благодаря этому уменьшаются объем кода, вероятность программных ошибок, время и стоимость разработки.
- Никаких изменений кода в целевом Web-приложении не требуется.
- Упрощается сопровождение и расширение Web-приложения. Новые требования реализуются в аспекте, затем внедряются в целевое Web-приложение. Изменение требований осуществляется также в коде реализации аспекта.
- Web-приложения полностью работоспособны без применения аспектов. При этом кодирование и декодирование текста, реализованные в аспекте, отсутствуют в целевых Web-приложениях.
5. Заключение
В данной работе описана и анализирована задача HTML-кодирования текста в ASP.NET-приложении. Предложен метод применения аспектно-ориентированного программирования для решения описанной задачи. Разработан аспект HTML-кодирования текста в системе Aspect.NET. Благодаря разработанному аспекту уменьшаются объем кода, вероятность программных ошибок, время и стоимость разработки.