• Microsoft .NET
  • ASP.NET
  • Замена URL с помощью ASP.NET для поисковой оптимизации

Замена URL с помощью ASP.NET для поисковой оптимизации - Создание более качественного обработчика HTTP

ОГЛАВЛЕНИЕ

Создание более качественного обработчика HTTP

Ниже приведена полная реализация обработчика HTTP, заменяющего ресурсы *.html на страницы *.aspx со строками запроса, с учетом высказанных ранее соображений.

public class BetterUrlRewriter : IHttpHandler, IRequiresSessionState
{
    const string ORIGINAL_PATHINFO = "UrlRewriterOriginalPathInfo";
    const string ORIGINAL_QUERIES = "UrlRewriterOriginalQueries";

    public void ProcessRequest(HttpContext context)
    {
     //Проверить, существует ли в действительности указанный файл HTML, и если да, обслужить его.
        String strReqPath = context.Server.MapPath
    (context.Request.AppRelativeCurrentExecutionFilePath);
        if (File.Exists(strReqPath))
        {
            context.Response.WriteFile(strReqPath);
            context.Response.End();
            return;
        }

        // Записать PathInfo исходного запроса и
        // информацию QueryString для обработки обратных отправок
        context.Items[ORIGINAL_PATHINFO] = context.Request.PathInfo;
        context.Items[ORIGINAL_QUERIES] = context.Request.QueryString.ToString();

        // Привязать удобный URL к серверному..
        String strVirtualPath = "";
        String strQueryString = "";
        MapFriendlyUrl(context, out strVirtualPath, out strQueryString);

        if(strVirtualPath.Length>0)
        {
            foreach (string strOriginalQuery in context.Request.QueryString.Keys)
            {
                // Чтобы обеспечить сохранение всех строк запроса, переданных в исходном
           // запросе, они добавляются в конец
                // новой строки запроса без добавления ключей,
           // замененных на протяжении обработчика.
                if (strQueryString.ToLower().IndexOf(strOriginalQuery.ToLower()
                                + "=") < 0)
                {
                    strQueryString += string.Format("{0}{1}={2}",
            ((strQueryString.Length > 0) ? "&" : ""),
            strOriginalQuery,
            context.Request.QueryString[strOriginalQuery]);
                }
            }

            // Применить требуемые строки запроса к запросу
            context.RewritePath(context.Request.Path, string.Empty, strQueryString);

    //Получить обработчик страницы для требуемой страницы ASPX с использованием этого контекста.
            Page aspxHandler = (Page)PageParser.GetCompiledPageInstance
        (strVirtualPath, context.Server.MapPath(strVirtualPath), context);

            // Выполнить обработчик.
            aspxHandler.PreRenderComplete +=
        new EventHandler(AspxPage_PreRenderComplete);
            aspxHandler.ProcessRequest(context);
        }
        else
        {
            // Привязка не найдена – генерируется ответ 404.

            context.Response.StatusCode = 404;
            context.Response.ContentType = "text/plain";
            context.Response.Write("Page Not Found");
            context.Response.End();
        }
    }

    void MapFriendlyUrl(HttpContext context, out String strVirtualPath,
                        out String strQueryString)
    {
        strVirtualPath = ""; strQueryString = "";

        // Сделать: Эта процедура должна проверять свойства context.Request и реализовать
        //       надлежащую систему привязки.
        //
        //       Присвоить strVirtualPath виртуальный путь целевой страницы aspx.
        //       Присвоить strQueryString все строки запроса, необходимые странице.

        if (context.Request.Path.IndexOf("FriendlyPage.html") >= 0)
        {
            // Пример жестко заданной привязки "FriendlyPage.html"
       // к "UnfriendlyPage.aspx"

            strVirtualPath = "~/UnfriendlyPage.aspx";
            strQueryString = "FirstQuery=1&SecondQuery=2";
        }
    }

    void AspxPage_PreRenderComplete(object sender, EventArgs e)
    {
        // Надо заменить путь, заменив исходный хвост и строки запроса.
        // Это происходит после загрузки и установки страницы,
        // но гарантирует, что
        // обратные отправки на страницу сохранят URL и запросы исходных незамененных страниц.

        HttpContext.Current.RewritePath(HttpContext.Current.Request.Path,
                        HttpContext.Current.Items[ORIGINAL_PATHINFO].ToString(),
                              HttpContext.Current.Items[ORIGINAL_QUERIES].ToString());
    }

    public bool IsReusable
    {
        get
        {
            return true;
        }
    }
}

Наряду с интерфейсом IHttpHandler задается интерфейс IRequiresSessionState. Это гарантирует получение доступа для чтения и записи к состоянию сессии на протяжении жизненного цикла страницы.

Сначала метод ProcessRequest проверяет, является ли запрошенный ресурс *.html на самом деле запросом к настоящему ресурсу HTML. В зависимости от требований эта проверка может быть ненужной, или она может требоваться после проверки привязок. Но она была включена здесь, так что, несмотря на то, что виртуальные страницы HTML, требующие замены, будут работать с использованием механизма замены, любые запросы к фактическим файлам HTML, действительно существующим в запрошенном месте, получают приоритет и обслуживаются.
Далее используется коллекция управления состоянием ключа/значения HttpContext.Items для сохранения строк запроса и хвоста текущего запроса, чтобы можно было извлечь их обратно в обработчике страницы PreRenderComplete. После вызова метода RewriteUrl для выполнения привязки написан дополнительный код, соединяющий все запросы, заданные в запросе, с запросами, требуемыми для привязки. Это делается так, чтобы заданные запросы привязки получали приоритет и не заменялись таким же параметром запроса из запроса – но при необходимости это можно изменить.

Наконец, если не удается получить привязку, то запрос считается недействительным, и для него выдается ответ 404, и обработка завершается. Можно показывать более качественный ответ, чем реализованный тут, или показывать карту сайта, и т.д.

Удачной замены!

Заключение

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

Что если нет доступа к IIS?

Представленная тут информация касается именно замены ресурсов *.html, а значит, требуется возможность добавления привязки в IIS для перенаправления ASP.NET запросов к *.html, однако можно использовать точно такой же процесс для замены любого типа ресурса - при условии, что запрос попадает в модуль ASP.NET ASAPI. Если ваше решение хостинга не позволяет добавлять привязки IIS или же у вас нет доступа к IIS, можно применять показанные тут методы замены с использованием другого типа обрабатываемого ресурса. Например, расширение *.ashx является первым кандидатом на использование вместо *.html, так как запросы *.ashx автоматически привязываются.