Технология динамического формирования статического контента

Материал предоставлен сайтом Территория Дмитрия Новоженова (http://www.novojonov.ru)

Проблема статического контента

При верстке любого сайта возникает одна простая проблема, которая, несмотря на свою простоту, требует решения. Это - проблема статического контента.

Статический контент в динамическом окружении стоит как-то особняком. Как правило, статические странички оформляются все равно как динамические, просто в их теле находится просто HTML-разметка контента. Но в этом-то и заключается проблема, а именно в том, что при попытке изменить внешний вид сайта придется переверстывать каждую из страниц статики. А делать это ой как лениво программисту, привыкшему к скинам и шаблонам =)

Решение проблемы

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

  1. Написать CMS и хранить тексты в динамике;
  2. Оформить весь текст пользовательскими элементами управления;
  3. Использовать XML;

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

Для поддержки принятого нами решения мы предпримем несколько шагов:

  1. Отформатируем наш контент как XML документ;
  2. Создадим XSLT шаблон для формирования частичного HTML из XML;
  3. Создадим ASP.NET шаблон для поддержки мастер-страниц и формирования вывода;
  4. Создадим HttpHandler для поддержки SFU (Search Friendly Url);

XML документ

Начало прогресса приходится на формирование схемы XML документа. Задача эта простая, но творческая =) Документ должен содержать тот набор смысловых тегов, который будет использоваться для структуризации материала. Утешением может служить то, что не обязательно формировать сразу всю схему, достаточно выделить минимальный набор и расширять его по ходу дела. Например, документ может быть таким:

<?xml version="1.0" encoding="utf-8" ?>
<article>
  <header>
    Заголовок статьи
  </header>
  <text>
    Текст параграфа статьи
  </text>
</article>

В данном примере тег article определяет корневой контейнер документа, тег header определяет заголовок а тег text определяет текст параграфа статьи. Для начала достаточно, другие добавим позже, если возникнет потребность.

Таким образом ны отделили содержание от внешнего вида и сохранили в файле XML только структуру документа, логику его разметки. Следующим шагом позаботимся о его отображении.

XSLT шаблон

Отображать документ из XML файла можно многими разными способами. Наиболее перспективным видится использование технологии XSLT, ведь мы окунулись в мир XML, так давайте окунаться с головой!

Для формирования уровня представления составим XSLT шаблон, назначением которого будет формирование HTML разметки нашего статического контента. Шаблон XSLT может быть, например, таким:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" omit-xml-declaration="yes" indent="no" />
  <xsl:template match="text()" />
  <xsl:template match="/">
    <xsl:apply-templates />
  </xsl:template>
  <xsl:template match="article">
    <xsl:apply-templates />
  </xsl:template>
  <xsl:template match="header">
    <div class="pagesubheader">
      <xsl:value-of select="text()" />
    </div>
  </xsl:template>
  <xsl:template match="text">
    <div class="pagetext">
      <xsl:value-of select="text()"/>
    </div>
  </xsl:template>
</xsl:stylesheet>

Это, безусловно, простейший шаблон, но для данного примера нам большего и не нужно. Стоит обратить внимание на следующие особенности шаблона:

  1. <xsl:output method="xml" omit-xml-declaration="yes" indent="no" /> - определение способа формирования вывода предусматривает подавление объявления XML, оно ни к чему, и подавляет формирование отступов, не все броузеры их одинаково отрабатывают;
  2. <xsl:template match="text()" /> - при обходе дерева XML встреченные текстовые ноды по умолчанию будут выводиться на выход, такое поведение не соответствует;
  3. <div class="pagetext"> - весь вывод осуществляется не конкретным стилевым оформлением а с использованием CSS классов, это создаст дополнительный уровень абстракции для формирования уровня отображения;

ASP.NET шаблон

Следующим шагом должно быть сведение воедино кусочков и формирование одного красивого и чистого целого =) Поскольку используются динамические технологии, страничка тоже будет динамической. Ее задача - оттранслировать наш XML с применением нашего шаблона. Страничка будет, например, такой:

<%@ Page Language="C#" MasterPageFile="~/default.master" %>

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        articleXml.DocumentSource = String.Format("~/articles/{0}/article.xml", Request["code"]);
        articleXml.TransformSource = "~/articles/article.xslt";

        articleXml.DataBind();
    }
</script>

<asp:Content ID="contentContent" ContentPlaceHolderID="contentPlace" runat="Server">
    <asp:Xml ID="articleXml" runat="server" />
</asp:Content>

Это также простейшая страничка но, тем не менее, она выполняет свою функцию. Она вписывает наш статический контент в динамический шаблон и, при этом, оформляет контент так, как мы просили.

Структура каталогов

Наша страничка нацелена на обработку URL вида ~/articles/article.aspx?code=topic для отображения статьи ~/articles/topic/article.xml. Для поддержки ее функционирования создадим следующую структуру файлов и каталогов:

  1. ~/articles - папка для статей;
  2. ~/articles/topic/article.xml - файл конкретной статьи;
  3. ~/articles/article.aspx - файл ASP.NET шаблона;
  4. ~/articles/article.xslt - файл XSLT шаблона;

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

HttpHandler

Получилось довольно неплохо, но есть что-то, что тревожит =) Ни пользователи ни поисковики не любят урлов с cgi-хвостами. Оно, конечно, нормально, но лучше было-бы, чтобы урлы были обычными, просто урлами себе и урлами. Теми, которые еще называют дружественными поисковикам и человеко-понятными.

Для решения этой проблемы воспользуемся технологией сервлетов, которую я подсмотрел при изучении Java. Все возможности для этого у нас в .NET имеются, спасибо Билли =)

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

using System;
using System.Web;

using System.IO;

namespace Soft
{
    public class Article : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            string code;

            code = context.Request.Url.Segments[context.Request.Url.Segments.Length - 2].Trim('/');

            if (File.Exists(context.Server.MapPath(String.Format("~/articles/{0}/article.xml", code))))
            {
                context.Server.Execute(String.Format("~/articles/article.aspx?code={0}", code),
                                                     context.Response.Output);
            }
            else
            {
                context.Response.Redirect("~/articles/articlelist.aspx");
            }
        }

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

Смысл этого обработчика в том, чтобы осуществить маршрутеризацию запроса. Запросы вида ~/articles/topic/article.aspx (такой страницы нет в нашей системе) будут транслированы в ~/articles/article.aspx?code=topic. Для тех запросов, для которых есть соответствующий XML. Запросы к несуществующим XML будут перенаправлены на страничку со списком статей. Обработчик необходимо зарегистрировать в web.config, например так:

<httpHandlers>
    <add verb="*" path="articles/*/article.aspx" type="Article" validate="false" />
</httpHandlers>

Теперь запросы будут перехвачены нашим обработчиком и отправлены к единственному шаблону, который оттранслирует нужный XML документ.

Таким образом мы свели дублирование кода к минимуму. Теперь остается только зарегистрировать статью с несуществующим URL =) в нашем файле SiteMap "и будет нам счестье".

Ложки не существует

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

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

Материал предоставлен сайтом Территория Дмитрия Новоженова (http://www.novojonov.ru)