Postback и Query String - совместить несовместимое

Я думаю любой из программистов, пишущих веб приложения с помощью веб форм, серверных элементов управления и событийно ориентированного программирования, согласится с утверждением, что данная технология очень сильно облегчила труд программистов. Но всякое событие/действие/программа имеет как положительные, так и отрицательные стороны. И одной из таких отрицательных сторон в нашем случае является крайне проблемтичное создание системы навигации, которое было бы одновременно и удобным для программирования (т.е. основывалось бы на серверных элементах управления) и удобным для поисковых роботов (состояло бы из набора банальных ссылок, содержащих хорошо понятные любому браузеру теги <a>). Сам я задумался об этом после того, как обнаружил резкое несоответствие реального количества страниц на мании и того количества страниц, которое находится в базах поисковых систем. А все из-за того, что списки сообщений в форумах у меня выводились с помощью элемента управления DataGrid с включенным постраничным выводом. Соотвественно пришло время немного подправить это упущение.

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

Для начала вспомним каком образом мы в прошлой статье генерировали код для инициирования клиентского постбека – используя метод Page.GetPostBackClientHyperlink. И я тогда еще отметил, что для этого также можно применять метод Page.GetPostBackEventReference – вся разница между результатами вызова этих 2-х методов заключается в строке “javascript:”, которая присутствует в начале возвращаемой строки результата вызова первого метода и отсутствует для второго метода. Соответственно вызов метода Page.GetPostBackClientHyperlink удобней всего применять при установке атрибута href ссылки, а Page.GetPostBackEventReference – при установке клиентского атрибута onclick. Соотв. первым шагом, который должен быть сделан для доработки элемента управления Pager будет модификация строки, вставляющей JavaScript в тег <a> - вместо

output.AddAttribute(HtmlTextWriterAttribute.Href, Page.GetPostBackClientHyperlink(this, (pgIndex - 1).ToString()));
output.RenderBeginTag(HtmlTextWriterTag.A);
output.Write(pgIndex.ToString());
output.RenderEndTag();

мы теперь будем писать

output.AddAttribute(HtmlTextWriterAttribute.Onclick, Page.GetPostBackEventReference(this, (pgIndex - 1).ToString()) + "; return false;");
output.RenderBeginTag(HtmlTextWriterTag.A);
output.Write(pgIndex.ToString());
output.RenderEndTag();

Но это еще не все изменения в ссылках на страницы – нам необходимо также добавить в эти ссылки атрибуты href чтобы любой браузер, даже не поддерживающий JS, мог переходить по страницам. Я данную процедуру забил жестко в код, но ничто не мешает вам сделать ее более красиво. Итак для ссылок я использовал параметр PageNum, который и определяет номер страницы.

Первым делом для генерации верных значений атрибутов href ссылок нам необходимо получить такуший урл страницы, затем произвести с ним некоторые манипуляции (возможно в урле уже содержится параметр PageNum – его нужно оттуда вырезать) и получившуюся строку мы можем использовать для создания нужных нам ссылок. Соответственно в первых строках метода Render пишем следующий код:

string pageUrl = Context.Request.RawUrl;
pageUrl = System.Text.RegularExpressions.Regex.Replace(pageUrl, @"&PageNum=(d+)", "");
pageUrl = System.Text.RegularExpressions.Regex.Replace(pageUrl, @"?PageNum=(d+)", "");
if(pageUrl.IndexOf("?") > -1)
      pageUrl += "&PageNum=";
else
      pageUrl += "?PageNum=";

Я думаю специально разбирать его смысла нет – он достаточно понятен и без этого :). Теперь нам всего лишь осталось использовать полученный pageUrl примерно так:

output.AddAttribute(HtmlTextWriterAttribute.Href, pageUrl + (pgIndex - 1).ToString());

Все, раздел генерации html кода полностью закончен. Теперь если пользователь с браузером, поддерживающим JS, нажмет на линк страницы – сработает клиентский скрипт из раздела onclick. Ну а для поискового робота (или браузера с отключенииым скриптом) всегда есть возможность пойти напрямую по ссылке :)

Осталось дело за малым – сделать так, чтобы наши ссылки успешно работали и событие перехода на новую страницу генерировалось и в случае перехода по ссылке с параметром, а не только при постбеке. И наиболее удачное для этого место это, как ни странно, начало метода RenderContents. Ведь нам нужно генерировать событие Navigate не только при наличии параметра в адресной строке, но и (что намного более важно) при постбеке, сгенерированном пейджером. Кроме того не нужно забывать и о такой вещи, как динамическое добавление данных на страницу, при котором нормальный порядок присутствия элементов управления на странице и отработки событий Init и Load нарушается, что может привести к непредсказуемой работе вполне вроде бы предсказуемых контролов. Посему в завершение добавим в начало метода RenderContents строки, которые проверяют не постбек ли данный рекыест и есть ли в адресной строке параметр PageNum и в случае выполнения этих условий устанавливают свойство CurrentPage элемента управления и вызывают метод RaisePostBackEvent:

if(!Page.IsPostBack && Context.Request.QueryString["PageNum"] != null)
{
            CurrentPage = Int32.Parse(Context.Request.QueryString["PageNum"]);
            RaisePostBackEvent(CurrentPage.ToString()); 
}

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

Выражаю свою признательность и благодарность Игорю Т. ака Legioner за идею :)

19 ноября 2006 в 14:10
Материалы по теме
{"url":"http://www.fastvps.ru/", "src":"/images/advbanners/fastvps.png", "alt":"Хостинг Fastvps.ru. Наш выбор!"}
Заработок