Полный контроль над выводом ваших веб-форм
Регулярные выражения очень полезны. Даже любитель может делать поразительные вещи при помощи всего лишь пары регулярных выражений.
Вывод веб-форм всегда был немного беспорядочным. Ваша страница обычно засорена множеством слишком длинных идентификаторов и имен содержимого, которые на самом деле не нужны (например, в программе разработчиков было имя accounts_overview_body_quickAction_quickActionTitleLabel для всего элемента диапазона).
Вы также испытываете трудности с ViewState (просмотр состояния), который может легко выйти из-под контроля. Его можно отключить, но он в любом случае отображает какое-то содержимое, которого может быть и не так много, но которое не нужно, а потому, возможно, вы захотите его убрать.
Одна из последних неприятных вещей в веб-формах заключается в том, что нельзя поместить дополнительные формы внутрь формы уровня страницы. Некоторые браузеры не возражают, но остальные не работают вообще. Если вам не нужны веб - элементы управления для выполнения чего-либо (например, сохранять их состояние), было бы хорошо иметь возможность полностью удалять их.
Получение контроля над вашей страницей
Путем замены события отображения на странице можно получить полный доступ к ее выводу до того, как оно будет отправлено пользователю.
Первый шаг – действительно отобразить содержимое, чтобы им можно было управлять. Без углубления в детали, мы собираемся записать наше содержимое в собственный локальный HtmlTextWriter (записывает текст и html), чтобы можно было управлять им до отправки его фактическому HtmlTextWriter. Это нетрудно сделать…
protected override void Render(HtmlTextWriter writer) {
//отображаем вывод
StringWriter output = new StringWriter();
HtmlTextWriter html = new HtmlTextWriter(output);
base.Render(html);
//получаем отображенную строку
string rendered = output.ToString();
//выполняем определенные изменения
//... описанные кратко
//удаляем объекты, выполняющие запись
html.Dispose();
output.Dispose();
//затем пишем его
writer.Write(rendered);
}
Строка получена, теперь можно начать выполнять изменения.
Отключение ViewState
Для некоторых страниц в действительности вообще не нужен ViewState. может быть, это информация о товаре или несколько фотографий, но на странице нет элементов управления, которые пользователь собирается отправить обратно. Нетрудно написать регулярное выражение для его удаления.
rendered = Regex.Replace(
rendered,
"<input.*name=\"__VIEWSTATE\"[^>]*>",
string.Empty,
RegexOptions.IgnoreCase
);
Этот маленький кусок кода полностью удалит ViewState со страницы. Его можно еще больше изменить, чтобы удалить другие элементы (например, __EVENTSTATE), изменив выражение на: "<input.*name=\"__\\w+\"[^>]*>".
Отключение формы страницы
Как было сказано ранее, если вы хотите поместить другие формы на страницу, то они должны быть вне формы страницы. Не все браузеры работают с вложенными формами.
При помощи веб-форм можно создать форму страницы, использовать все элементы управления внутри нее и затем удалить разметку, перед тем как она будет отправлена клиенту. Таким образом, можно использовать веб-элементы управления и вложенные формы.
Этот процесс немного более сложный.
//ищем все формы и закрытые элементы
MatchCollection matches = Regex.Matches(
rendered,
"</?form[^>]*>",
RegexOptions.IgnoreCase
);
//создает выражение, совпадающее с идентификатором
//ведущие страницы переименовывают форму в 'aspnetForm' – это странно
Regex expectedId = new Regex(
string.Format("id=\"?({0}|aspnetForm)\"?", this.Form.ID),
RegexOptions.IgnoreCase
);
//выражение для проверки закрытия формы
Regex closeForm = new Regex(
@"</\s?form\s?>",
RegexOptions.IgnoreCase
);
//сквозной цикл и удаление формы страницы
Match open = null;
Match close = null;
int depth = 0;
//проверяем на наличие открытой формы, затем проверяем на
//совпадение с тегом закрывания (проверка на вложение)
for (int i = 0; i < matches.Count; i++) {
Match match = matches[i];
//проверяем, является ли эта форма формой страницы
if (expectedId.IsMatch(match.Value) && !(open is Match)) {
open = match;
}
//если еще не нашли формы страницы, продолжаем цикл
if (!(open is Match)) { continue; }
//меняем направление вложения
depth += closeForm.IsMatch(match.Value) ? -1 : 1;
//если вложение равняется нулю, можно считать, что это
//верный тег
if (depth == 0) {
close = match;
break;
}
}
//удаление тегов – обязательно начинайте с тега закрытия,
//так как это повлияет на индекс совпадений
if (open is Match && close is Match) {
rendered = rendered.Remove(close.Index, close.Length);
rendered = rendered.Remove(open.Index, open.Length);
}
При помощи этого кода можно удалить форму страницы со страницы, но сохраните все остальные формы, чтобы они могли обрабатывать отправку данных обратно в самих себя, когда бы они ни увидели соответствие.
Стоит отметить, что, как и удаление ViewState (просмотр состояния), postbacks (отправка данных из формы обратно в эту же форму) и WebControls (веб -элементы управления), которые опираются на эти функции, скорее всего, больше не будут работать (или, по крайней мере, не будут работать как ожидалось).
Сокращение идентификаторов
ASP.NET 4.0 собирается предоставить несколько удобных функций, помогающих разработчикам отправлять осмысленные идентификаторы клиенту. Сейчас идентификаторы создаются путем использования родителей и идентификаторов. Предотвращать столкновения имен замечательно – но это ужасно при попытке сделать что-либо на стороне клиента. Не только это, но иногда идентификаторы оказываются присвоенными элементам, с которыми вы даже не собираетесь работать, то есть место занимается впустую.
С помощью регулярных выражений можно изменять идентификаторы на странице.
//наконец, удаляем все ненужные идентификаторы
MatchCollection extraIds = Regex.Matches(
rendered,
@"<[^>]*actualId=""(?<id>[^""]*)""[^>]*>",
RegexOptions.IgnoreCase
);
//выполняем обратный цикл для предотвращения воздействия на индексы на совпадениях
for (int i = extraIds.Count; i-- > 0; ) {
Match extra = extraIds[i];
//удаление ненужных частей/
string newElement = extra.Value;
string newID = extra.Groups["id"].Value;
//наконец, удаляем фактическое поле идентификатора
newElement = Regex.Replace(
newElement,
@"actualId=""[^""]*"" ?",
string.Empty,
RegexOptions.IgnoreCase
);
//если идентификатор пустой, просто удаляем его
newElement = Regex.Replace(newElement, @"(id|name)=""[^""]*"" ?", (str) => {
if (string.IsNullOrEmpty(newID)) { return string.Empty; }
return string.Concat(
str.Value.StartsWith("id", StringComparison.OrdinalIgnoreCase) ? "id" : "name",
"=\"",
newID,
"\" "
);
});
//наконец, заменяем на новую строку
rendered = rendered.Remove(extra.Index, extra.Length);
rendered = rendered.Insert(extra.Index, newElement);
}
Несомненно, это специальное решение, мягко говоря. Идея заключается в том, что теперь можно добавлять свойство “actualId (фактический идентификатор)” в элементы управления, и атрибуты идентификатор и имя will будут изменяться, чтобы отображать то значение. Кроме того, атрибут actualId будет полностью удален из элемента.
Можно внести в код еще больше изменений, чтобы разобрать все идентификаторы, которые не помечены каким-то образом, чтобы сохранить их идентификаторы.
Неограниченная власть разрушает
Интересно войти в отображенный вывод и вносить изменения в то, что отправляется клиенту. Вы имеете много возможностей по полному изменению того, как выглядит ваше содержимое. Но будьте внимательны – чем больше вы изменяете, тем скорее вы разрушите метод работы ASP.NET.
Примечание: Описанные способы не работают с UpdatePanels (обновляющие панели) — по крайней мере, не работают без дополнительных усилий. Содержимое из UpdatePanels разделяется символами вертикальной линии и имеет подсчет числа знаков для содержимого со всеми идентификаторами, которые он возвращает. Было бы довольно трудно внести изменения, не разрушив ничего (однако это возможно!).