Всплывающие надписи. Как сделать всплывающую подсказку
Виртуальные пути
Между виртуальными и физическими путями существует стандартное отображение. Виртуальный путь наподобие /Content/RequestReporter.aspx соответствует файлу веб-формы /Content/RequestReporter.aspx. Главное преимущество такого отображения заключается в простоте - достаточно взглянуть на URL и немедленно понять, каким образом виртуальный путь будет применяться для генерации ответа.
Однако прямое отображение между виртуальными и физическими путями не всегда идеально. Например, модернизация приложения может привести к перемещению и переименованию файлов, из-за чего перестанут работать URL, помещенные пользователями в закладки. Может потребоваться согласование приложения Web Forms с более широкой схемой URL, которая не соответствует стандартному подходу. Подобная ситуация распространена в корпоративных приложениях, в которых согласованность URL расценивается как способ помочь пользователям в переходе непосредственно к нужному контенту или функциональности.
Короче говоря, существует множество причин для изменения URL, на которые реагирует приложение, и все они приводят к нарушению работы прямых ссылок между виртуальными и физическими путями. В последующих разделах мы опишем разнообразные подходы, доступные для управления виртуальными путями и позволяющие настраивать схему URL, которую приложение раскрывает внешнему миру.
Вы узнаете, что есть много способов достижения того же самого базового результата. Причина в том, что все эти средства постепенно добавлялись с течением времени. Самое последнее добавление - поддержка маршрутизации URL - является тем средством, с которого следует начинать. Однако мы также рассматриваем ряд удобных старых средств, т.к. часто требуется комбинировать одно из них с маршрутизацией URL, чтобы создавать специфическую функциональность в приложении.
Установка стандартных документов
По соглашению, принятому для приложений Web Forms, начальной веб-форме назначается имя Default.aspx. Это не является требованием, но его нужно придерживаться, поскольку сервер IIS сконфигурирован так, что он ищет стандартные файлы, если никакого файла в URL не указано.
Чтобы продемонстрировать это в действии, мы внесли в метод ProcessRequest(), определенный в файле SimpleModule.cs (который мы создали в предыдущей статье), небольшое изменение, которое обеспечивает вывод значения свойства FilePath и URL в окно Output среды Visual Studio. Изменение показано в примере ниже:
// ... private void ProcessRequest(HttpApplication app) { if (app.Request.FilePath == "/Test.aspx") { app.Server.Transfer("/Content/RequestReporter.aspx"); } WriteMsg("URL запроса: {0} {1}", app.Request.RawUrl, app.Request.FilePath); } // ...
Для тестирования встроенного поведения запустите приложение и запросите корневой URL (у нас он выглядит как http://localhost:32404/, но у вас может быть другой номер порта). Браузер отобразит содержимое файла Default.aspx, а в окне Output будут отображаться следующие сообщения:
Последнее из этих сообщений отражает попытку сервера IIS найти файл, с помощью которого должен быть обслужен запрос. (Первые два сообщения объясняются в следующем разделе.) Сервер IIS имеет возможность отыскать файл Default.aspx для обслуживания запроса корневого URL, т.к. мы соблюдали соглашение об именовании. В противном случае IIS Express потерпел бы неудачу и возвратил браузеру ошибку 404, указывающую на то, что файл найти не удалось.
Сервер IIS ищет следующие стандартные документы: Default.html, Default.asp, index.htm, index.html, iisstart.htm и, наконец, default.aspx. (Мы не знаем, почему имя файла default.aspx представлено символами нижнего регистра, но это не играет роли, потому что имена веб-форм нечувствительны к регистру.)
Переопределить эти стандартные документы можно в файле Web.config. Это стоит делать, если нужно, чтобы по умолчанию использовалась другая стандартная веб-форма, или требуется сократить количество местоположений, в которые сервер IIS просматривает, прежде чем передает веб-форму на обработку среде ASP.NET.
В примере ниже показаны изменения, внесенные в файл Web.config:
Элемент defaultDocument
добавлен в раздел
Внутри defaultDocument содержится элемент
Обратите внимание, что при указании стандартного документа в атрибуте value ведущий символ / отсутствует. Добавление ведущего символа / приводит к отображению сообщения об ошибке.
Чтобы увидеть результат, запустите приложение и запросите URL вида http://localhost:<порт>/, где <порт> это номер порта, прослушиваемого сервером IIS Express на предмет поступления запросов для этого приложения. Новая политика в отношении стандартного документа будет применена, и отобразится вывод, сгенерированный веб-формой RequestReporter.aspx.
Обработка запросов для URL без расширений
При отправке запроса к корневому URL в предыдущем разделе в окне Output среды Visual Studio отображались три сообщения:
URL запроса: / / URL запроса: / / URL запроса: / /default.aspx
Мы объяснили, что последнее сообщение говорит о применении политики IIS, касающейся стандартного документа. Первоначально это был запрос к Default.aspx, но мы изменили политику IIS в отношении стандартного документа. Перед тем, как IIS применяет такую политику, он предоставляет ASP.NET шанс обработать запрос - это причина, по которой отображено первое сообщение.
Среда ASP.NET имеет обработчик для этого запроса, однако по умолчанию он не делает ничего полезного. Таким обработчиком является внутренний класс TransferRequestHandler , отвечающий за обработку URL без расширений, которые позволяют ASP.NET обрабатывать запросы к виртуальным путям, не содержащим файловые расширения, такие как.aspx.
Класс TransferRequestHandler не делает особенно много с запросами для URL без расширений. Он просто указывает серверу IIS на необходимость создания и обработки второго запроса, не используя TransferRequestHandler в качестве обработчика - именно поэтому отображается второе сообщение.
Чтобы делать нечто полезное с запросами для URL без расширений, понадобится создать обработчик и заменить им TransferRequestHandler. Мы добавили в проект новый файл класса по имени ExtensionlessHandler.cs, содержимое которого приведено в примере ниже:
Using System.IO; using System.Web; namespace PathsAndURLs { public class ExtensionlessHandler: IHttpHandler { public void ProcessRequest(HttpContext context) { context.Response.Write("
HTTP-обработчик Expressionless
"); string vpath = context.Request.Path; if (vpath == "/") { context.Server.Transfer("/Default.aspx"); } else if (File.Exists(context.Request.MapPath(vpath + ".aspx"))) { context.Server.Transfer(vpath + ".aspx"); } else { context.Response.StatusCode = 404; context.ApplicationInstance.CompleteRequest(); } } public bool IsReusable { get { return false; } } } }Этот обработчик будет получать запросы для URL без расширений и с помощью метода HttpServerUtility.Transfer() передавать запросы веб-форме. Определение, какой веб-форме должен быть передан запрос, реализуется элементарно. Если запрошенным URL является /, мы направляем его Default.aspx, а для всех других запросов просто добавляем расширение.aspx к запрошенному URL и проверяем, существует ли в приложении файл с таким именем. Если это так, мы передаем ему запрос, а в противном случае возвращаем ответ с ошибкой 404.
Чтобы обработчик смог принимать запросы, его необходимо зарегистрировать. В примере ниже показаны изменения, внесенные в файл Web.config:
Для обработки URL без расширений мы устанавливаем атрибут path в "*." (символы звездочки и точки). Обработка URL без расширений выполняется перед применением политики IIS, касающейся стандартного документа, поэтому предыдущая конфигурация была переопределена и корневой URL (/) отображен на Default.aspx. В качестве дополнения мы можем запрашивать любую веб-форму, не указывая файловое расширение. Таким образом, например, запрос для виртуального пути /Content/RequestReporter сгенерирует ответ на основе веб-формы /Content/RequestReporter.aspx.
Переписывание путей
В предыдущем примере использовался метод HttpServerUtility.Transfer(), который нормально работает с веб-формами, но не очень хорошо с другими типами файлов, такими как обобщенные обработчики (файлы ashx). Мы могли бы применить прием с оболочкой для Page, но это грубый трюк, и мы не являемся его сторонниками.
Имея все это в виду, рассматриваемый далее подход может быть реализован более широко, однако это должно делаться внутри модуля. Прием называется переписыванием путей и представляет собой просто процесс изменения пути, связанного с запросом. Чтобы показать пример использования такого подхода, мы создали файл класса по имени PathModule.cs с содержимым, приведенным в примере ниже:
Using System; using System.IO; using System.Web; namespace PathsAndURLs { public class PathModule: IHttpModule { private static readonly string extensions = { ".aspx", ".ashx" }; public void Init(HttpApplication app) { app.BeginRequest += (src, args) => HandleRequest(app); } private void HandleRequest(HttpApplication app) { if (app.Request.CurrentExecutionFilePathExtension == String.Empty) { string target = null; string vpath = app.Request.CurrentExecutionFilePath; if (vpath == "/") { target = "/Default.aspx"; } else { foreach (string ext in extensions) { if (File.Exists(app.Request.MapPath(vpath + ext))) { target = vpath + ext; break; } } } if (target != null) { app.Context.RewritePath(target); } } } public void Dispose() { // Ничего не освобождается } } }
Так как это модуль, его необходимо зарегистрировать в файле Web.config:
Этот модуль обрабатывает событие BeginRequest и просматривает запросы, которые не имеют файлового расширения. В рассматриваемом примере изменяется способ обработки корневого URL, т.е. для обработки запросов применяется веб-форма Default.aspx, которую можно увидеть, запустив приложение и запросив /.
Главное улучшение по сравнению с предыдущим примером связано с проверкой существования файлов, имеющих расширения aspx или ashx, если запрошенным URL не является /. Это позволяет приложению поддерживать дружественные URL - именно так называются запросы веб-форм и обработчиков без указания файловых расширений. (Происхождение данного названия нам не известно, однако мы его придерживаемся.)
Прием является вариацией обработки URL без расширений и по-прежнему требует сопоставления виртуальных путей с файлами в приложении, но без указания расширений.aspx или.ashx, которые многие разработчики считают непривлекательными.
Ключевым в модуле является метод RewritePath() , который определен в классе HttpContext. Этот метод позволяет изменить путь при условии, что это делается перед событием жизненного цикла MapRequestHandler .
Метод RewritePath() не имеет ограничений, с которыми мы сталкиваемся при использовании методов класса HttpServerUtility, т.е. появляется возможность поддерживать запросы к обобщенным обработчикам, а также файлам веб-форм.
В классе HttpContext определено несколько перегруженных версий метода RewritePath(), которые описаны в таблице ниже:
Две перегруженных версии метода RewritePath() принимают аргумент типа bool по имени rebase, который отвечает за изменение путей, используемых элементами управления для создания URL - процесс, известный как изменение базы клиента .
В Microsoft предлагается загружаемый пакет под названием URL Rewriting Engine (Механизм переписывания URL) , который позволяет выражать правила переписывания в файле Web.config, а не в коде.
Реальный пример переписывания путей
Ранее метод HttpContext.RewritePath() использовался для добавления файлового расширения, т.е. мы могли бы поддерживать URL без расширений и дружественные URL. С помощью средства переписывания путей можно реализовать очень сложные действия, из-за чего мы применяем его в проектах, требующих поддержки необычных схем URL и таких, которые варьируются на основе характеристик запросов, отличных от путей.
В целях иллюстрации мы рассмотрим пример, основанный на одном из реальных проектов, над которым мы работали недавно. Нам необходимо было повторно построить приложение с применением ASP.NET, но старые URL были жестко закодированы в других приложениях и должны были остаться работоспособными.
Одна из проблем, с которой мы столкнулись, заключалась в том, что запросы к URL вида /accounts нужно было направлять двум веб-формам в зависимости от значения данных формы, имеющего имя function. Когда значение function не превышало 100, запрос должен отправляться веб-форме Default.aspx, а для всех других значений - веб-форме /Content/RequestReporter.aspx (разумеется, это не веб-формы из реального проекта; мы просто хотим воспользоваться уже готовыми файлами в примере приложения).
Чтобы продемонстрировать проблему, мы создали новую веб-форму по имени Split.aspx, контент которой представлен в примере ниже. Эта веб-форма будет эмулировать унаследованных клиентов с жестко закодированными URL:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Split.aspx.cs" Inherits="PathsAndURLs.Split" %>
Веб-форма содержит простую HTML-форму, которая позволяет ввести значение function и отправить его серверу по щелчку на кнопке "Отправить". Элемент
Готовый код: